sidebar: Remove AgentSessionInfo usage (#52732)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com> Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
1 parent
8ae53240a4
commit
8d39d9e4e0
4 files changed
+226
-359
No files matched your search
@@ -105,6 +105,19 @@ pub struct ThreadMetadata {
|
||||
pub archived: bool,
|
||||
}
|
||||
|
||||
impl From<&ThreadMetadata> for acp_thread::AgentSessionInfo {
|
||||
fn from(meta: &ThreadMetadata) -> Self {
|
||||
Self {
|
||||
session_id: meta.session_id.clone(),
|
||||
work_dirs: Some(meta.folder_paths.clone()),
|
||||
title: Some(meta.title.clone()),
|
||||
updated_at: Some(meta.updated_at),
|
||||
created_at: meta.created_at,
|
||||
meta: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadMetadata {
|
||||
pub fn from_thread(
|
||||
is_archived: bool,
|
||||
|
||||
+112
-239
@@ -4,7 +4,7 @@ use acp_thread::ThreadStatus;
|
||||
use action_log::DiffStats;
|
||||
use agent_client_protocol::{self as acp};
|
||||
use agent_settings::AgentSettings;
|
||||
use agent_ui::thread_metadata_store::ThreadMetadataStore;
|
||||
use agent_ui::thread_metadata_store::{ThreadMetadata, ThreadMetadataStore};
|
||||
use agent_ui::threads_archive_view::{
|
||||
ThreadsArchiveView, ThreadsArchiveViewEvent, format_history_entry_timestamp,
|
||||
};
|
||||
@@ -92,19 +92,6 @@ struct ActiveThreadInfo {
|
||||
diff_stats: DiffStats,
|
||||
}
|
||||
|
||||
impl From<&ActiveThreadInfo> for acp_thread::AgentSessionInfo {
|
||||
fn from(info: &ActiveThreadInfo) -> Self {
|
||||
Self {
|
||||
session_id: info.session_id.clone(),
|
||||
work_dirs: None,
|
||||
title: Some(info.title.clone()),
|
||||
updated_at: Some(Utc::now()),
|
||||
created_at: Some(Utc::now()),
|
||||
meta: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ThreadEntryWorkspace {
|
||||
Open(Entity<Workspace>),
|
||||
@@ -120,8 +107,7 @@ struct WorktreeInfo {
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ThreadEntry {
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
icon: IconName,
|
||||
icon_from_external_svg: Option<SharedString>,
|
||||
status: AgentThreadStatus,
|
||||
@@ -141,7 +127,7 @@ impl ThreadEntry {
|
||||
/// but if we have a correspond thread already loaded we want to apply the
|
||||
/// live information.
|
||||
fn apply_active_info(&mut self, info: &ActiveThreadInfo) {
|
||||
self.session_info.title = Some(info.title.clone());
|
||||
self.metadata.title = info.title.clone();
|
||||
self.status = info.status;
|
||||
self.icon = info.icon;
|
||||
self.icon_from_external_svg = info.icon_from_external_svg.clone();
|
||||
@@ -191,7 +177,7 @@ impl ListEntry {
|
||||
|
||||
fn session_id(&self) -> Option<&acp::SessionId> {
|
||||
match self {
|
||||
ListEntry::Thread(thread_entry) => Some(&thread_entry.session_info.session_id),
|
||||
ListEntry::Thread(thread_entry) => Some(&thread_entry.metadata.session_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -690,7 +676,7 @@ impl Sidebar {
|
||||
.iter()
|
||||
.filter_map(|entry| match entry {
|
||||
ListEntry::Thread(thread) if thread.is_live => {
|
||||
Some((thread.session_info.session_id.clone(), thread.status))
|
||||
Some((thread.metadata.session_id.clone(), thread.status))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
@@ -710,7 +696,7 @@ impl Sidebar {
|
||||
.iter()
|
||||
.any(|ws| !workspace_path_list(ws, cx).paths().is_empty());
|
||||
|
||||
let resolve_agent = |agent_id: &AgentId| -> (Agent, IconName, Option<SharedString>) {
|
||||
let resolve_agent_icon = |agent_id: &AgentId| -> (IconName, Option<SharedString>) {
|
||||
let agent = Agent::from(agent_id.clone());
|
||||
let icon = match agent {
|
||||
Agent::NativeAgent => IconName::ZedAgent,
|
||||
@@ -719,7 +705,7 @@ impl Sidebar {
|
||||
let icon_from_external_svg = agent_server_store
|
||||
.as_ref()
|
||||
.and_then(|store| store.read(cx).agent_icon(&agent_id));
|
||||
(agent, icon, icon_from_external_svg)
|
||||
(icon, icon_from_external_svg)
|
||||
};
|
||||
|
||||
for (group_name, group) in project_groups.groups() {
|
||||
@@ -768,19 +754,11 @@ impl Sidebar {
|
||||
if !seen_session_ids.insert(row.session_id.clone()) {
|
||||
continue;
|
||||
}
|
||||
let (agent, icon, icon_from_external_svg) = resolve_agent(&row.agent_id);
|
||||
let (icon, icon_from_external_svg) = resolve_agent_icon(&row.agent_id);
|
||||
let worktrees =
|
||||
worktree_info_from_thread_paths(&row.folder_paths, &project_groups);
|
||||
threads.push(ThreadEntry {
|
||||
agent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
session_id: row.session_id.clone(),
|
||||
work_dirs: None,
|
||||
title: Some(row.title.clone()),
|
||||
updated_at: Some(row.updated_at),
|
||||
created_at: row.created_at,
|
||||
meta: None,
|
||||
},
|
||||
metadata: row,
|
||||
icon,
|
||||
icon_from_external_svg,
|
||||
status: AgentThreadStatus::default(),
|
||||
@@ -818,19 +796,11 @@ impl Sidebar {
|
||||
if !seen_session_ids.insert(row.session_id.clone()) {
|
||||
continue;
|
||||
}
|
||||
let (agent, icon, icon_from_external_svg) = resolve_agent(&row.agent_id);
|
||||
let (icon, icon_from_external_svg) = resolve_agent_icon(&row.agent_id);
|
||||
let worktrees =
|
||||
worktree_info_from_thread_paths(&row.folder_paths, &project_groups);
|
||||
threads.push(ThreadEntry {
|
||||
agent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
session_id: row.session_id.clone(),
|
||||
work_dirs: None,
|
||||
title: Some(row.title.clone()),
|
||||
updated_at: Some(row.updated_at),
|
||||
created_at: row.created_at,
|
||||
meta: None,
|
||||
},
|
||||
metadata: row,
|
||||
icon,
|
||||
icon_from_external_svg,
|
||||
status: AgentThreadStatus::default(),
|
||||
@@ -862,11 +832,11 @@ impl Sidebar {
|
||||
// Merge live info into threads and update notification state
|
||||
// in a single pass.
|
||||
for thread in &mut threads {
|
||||
if let Some(info) = live_info_by_session.get(&thread.session_info.session_id) {
|
||||
if let Some(info) = live_info_by_session.get(&thread.metadata.session_id) {
|
||||
thread.apply_active_info(info);
|
||||
}
|
||||
|
||||
let session_id = &thread.session_info.session_id;
|
||||
let session_id = &thread.metadata.session_id;
|
||||
|
||||
let is_thread_workspace_active = match &thread.workspace {
|
||||
ThreadEntryWorkspace::Open(thread_workspace) => active_workspace
|
||||
@@ -890,16 +860,16 @@ impl Sidebar {
|
||||
threads.sort_by(|a, b| {
|
||||
let a_time = self
|
||||
.thread_last_message_sent_or_queued
|
||||
.get(&a.session_info.session_id)
|
||||
.get(&a.metadata.session_id)
|
||||
.copied()
|
||||
.or(a.session_info.created_at)
|
||||
.or(a.session_info.updated_at);
|
||||
.or(a.metadata.created_at)
|
||||
.or(Some(a.metadata.updated_at));
|
||||
let b_time = self
|
||||
.thread_last_message_sent_or_queued
|
||||
.get(&b.session_info.session_id)
|
||||
.get(&b.metadata.session_id)
|
||||
.copied()
|
||||
.or(b.session_info.created_at)
|
||||
.or(b.session_info.updated_at);
|
||||
.or(b.metadata.created_at)
|
||||
.or(Some(b.metadata.updated_at));
|
||||
b_time.cmp(&a_time)
|
||||
});
|
||||
} else {
|
||||
@@ -920,12 +890,7 @@ impl Sidebar {
|
||||
|
||||
let mut matched_threads: Vec<ThreadEntry> = Vec::new();
|
||||
for mut thread in threads {
|
||||
let title = thread
|
||||
.session_info
|
||||
.title
|
||||
.as_ref()
|
||||
.map(|s| s.as_ref())
|
||||
.unwrap_or("");
|
||||
let title: &str = &thread.metadata.title;
|
||||
if let Some(positions) = fuzzy_match_positions(&query, title) {
|
||||
thread.highlight_positions = positions;
|
||||
}
|
||||
@@ -960,7 +925,7 @@ impl Sidebar {
|
||||
});
|
||||
|
||||
for thread in matched_threads {
|
||||
current_session_ids.insert(thread.session_info.session_id.clone());
|
||||
current_session_ids.insert(thread.metadata.session_id.clone());
|
||||
entries.push(thread.into());
|
||||
}
|
||||
} else {
|
||||
@@ -1011,7 +976,7 @@ impl Sidebar {
|
||||
for (index, thread) in threads.into_iter().enumerate() {
|
||||
let is_hidden = index >= count;
|
||||
|
||||
let session_id = &thread.session_info.session_id;
|
||||
let session_id = &thread.metadata.session_id;
|
||||
if is_hidden {
|
||||
let is_promoted = thread.status == AgentThreadStatus::Running
|
||||
|| thread.status == AgentThreadStatus::WaitingForConfirmation
|
||||
@@ -1861,22 +1826,15 @@ impl Sidebar {
|
||||
self.toggle_collapse(&path_list, window, cx);
|
||||
}
|
||||
ListEntry::Thread(thread) => {
|
||||
let session_info = thread.session_info.clone();
|
||||
let metadata = thread.metadata.clone();
|
||||
match &thread.workspace {
|
||||
ThreadEntryWorkspace::Open(workspace) => {
|
||||
let workspace = workspace.clone();
|
||||
self.activate_thread(
|
||||
thread.agent.clone(),
|
||||
session_info,
|
||||
&workspace,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.activate_thread(metadata, &workspace, window, cx);
|
||||
}
|
||||
ThreadEntryWorkspace::Closed(path_list) => {
|
||||
self.open_workspace_and_activate_thread(
|
||||
thread.agent.clone(),
|
||||
session_info,
|
||||
metadata,
|
||||
path_list.clone(),
|
||||
window,
|
||||
cx,
|
||||
@@ -1942,8 +1900,7 @@ impl Sidebar {
|
||||
|
||||
fn load_agent_thread_in_workspace(
|
||||
workspace: &Entity<Workspace>,
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: &ThreadMetadata,
|
||||
focus: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
@@ -1955,10 +1912,10 @@ impl Sidebar {
|
||||
if let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) {
|
||||
agent_panel.update(cx, |panel, cx| {
|
||||
panel.load_agent_thread(
|
||||
agent,
|
||||
session_info.session_id,
|
||||
session_info.work_dirs,
|
||||
session_info.title,
|
||||
Agent::from(metadata.agent_id.clone()),
|
||||
metadata.session_id.clone(),
|
||||
Some(metadata.folder_paths.clone()),
|
||||
Some(metadata.title.clone()),
|
||||
focus,
|
||||
window,
|
||||
cx,
|
||||
@@ -1969,8 +1926,7 @@ impl Sidebar {
|
||||
|
||||
fn activate_thread_locally(
|
||||
&mut self,
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: &ThreadMetadata,
|
||||
workspace: &Entity<Workspace>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -1982,40 +1938,32 @@ impl Sidebar {
|
||||
// Set focused_thread eagerly so the sidebar highlight updates
|
||||
// immediately, rather than waiting for a deferred AgentPanel
|
||||
// event which can race with ActiveWorkspaceChanged clearing it.
|
||||
self.focused_thread = Some(session_info.session_id.clone());
|
||||
self.record_thread_access(&session_info.session_id);
|
||||
self.focused_thread = Some(metadata.session_id.clone());
|
||||
self.record_thread_access(&metadata.session_id);
|
||||
|
||||
multi_workspace.update(cx, |multi_workspace, cx| {
|
||||
multi_workspace.activate(workspace.clone(), cx);
|
||||
});
|
||||
|
||||
Self::load_agent_thread_in_workspace(workspace, agent, session_info, true, window, cx);
|
||||
Self::load_agent_thread_in_workspace(workspace, metadata, true, window, cx);
|
||||
|
||||
self.update_entries(cx);
|
||||
}
|
||||
|
||||
fn activate_thread_in_other_window(
|
||||
&self,
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
workspace: Entity<Workspace>,
|
||||
target_window: WindowHandle<MultiWorkspace>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let target_session_id = session_info.session_id.clone();
|
||||
let target_session_id = metadata.session_id.clone();
|
||||
|
||||
let activated = target_window
|
||||
.update(cx, |multi_workspace, window, cx| {
|
||||
window.activate_window();
|
||||
multi_workspace.activate(workspace.clone(), cx);
|
||||
Self::load_agent_thread_in_workspace(
|
||||
&workspace,
|
||||
agent,
|
||||
session_info,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
Self::load_agent_thread_in_workspace(&workspace, &metadata, true, window, cx);
|
||||
})
|
||||
.log_err()
|
||||
.is_some();
|
||||
@@ -2040,8 +1988,7 @@ impl Sidebar {
|
||||
|
||||
fn activate_thread(
|
||||
&mut self,
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
workspace: &Entity<Workspace>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -2050,7 +1997,7 @@ impl Sidebar {
|
||||
.find_workspace_in_current_window(cx, |candidate, _| candidate == workspace)
|
||||
.is_some()
|
||||
{
|
||||
self.activate_thread_locally(agent, session_info, &workspace, window, cx);
|
||||
self.activate_thread_locally(&metadata, &workspace, window, cx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2060,13 +2007,12 @@ impl Sidebar {
|
||||
return;
|
||||
};
|
||||
|
||||
self.activate_thread_in_other_window(agent, session_info, workspace, target_window, cx);
|
||||
self.activate_thread_in_other_window(metadata, workspace, target_window, cx);
|
||||
}
|
||||
|
||||
fn open_workspace_and_activate_thread(
|
||||
&mut self,
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
path_list: PathList,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -2084,7 +2030,7 @@ impl Sidebar {
|
||||
let workspace = open_task.await?;
|
||||
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.activate_thread(agent, session_info, &workspace, window, cx);
|
||||
this.activate_thread(metadata, &workspace, window, cx);
|
||||
})?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
@@ -2113,31 +2059,23 @@ impl Sidebar {
|
||||
|
||||
fn activate_archived_thread(
|
||||
&mut self,
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
ThreadMetadataStore::global(cx).update(cx, |store, cx| {
|
||||
store.unarchive(&session_info.session_id, cx)
|
||||
});
|
||||
ThreadMetadataStore::global(cx)
|
||||
.update(cx, |store, cx| store.unarchive(&metadata.session_id, cx));
|
||||
|
||||
if let Some(path_list) = &session_info.work_dirs {
|
||||
if let Some(workspace) = self.find_current_workspace_for_path_list(path_list, cx) {
|
||||
self.activate_thread_locally(agent, session_info, &workspace, window, cx);
|
||||
if !metadata.folder_paths.paths().is_empty() {
|
||||
let path_list = metadata.folder_paths.clone();
|
||||
if let Some(workspace) = self.find_current_workspace_for_path_list(&path_list, cx) {
|
||||
self.activate_thread_locally(&metadata, &workspace, window, cx);
|
||||
} else if let Some((target_window, workspace)) =
|
||||
self.find_open_workspace_for_path_list(path_list, cx)
|
||||
self.find_open_workspace_for_path_list(&path_list, cx)
|
||||
{
|
||||
self.activate_thread_in_other_window(
|
||||
agent,
|
||||
session_info,
|
||||
workspace,
|
||||
target_window,
|
||||
cx,
|
||||
);
|
||||
self.activate_thread_in_other_window(metadata, workspace, target_window, cx);
|
||||
} else {
|
||||
let path_list = path_list.clone();
|
||||
self.open_workspace_and_activate_thread(agent, session_info, path_list, window, cx);
|
||||
self.open_workspace_and_activate_thread(metadata, path_list, window, cx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2150,7 +2088,7 @@ impl Sidebar {
|
||||
});
|
||||
|
||||
if let Some(workspace) = active_workspace {
|
||||
self.activate_thread_locally(agent, session_info, &workspace, window, cx);
|
||||
self.activate_thread_locally(&metadata, &workspace, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2306,7 +2244,7 @@ impl Sidebar {
|
||||
// a blank new thread in the panel instead.
|
||||
if self.focused_thread.as_ref() == Some(session_id) {
|
||||
let current_pos = self.contents.entries.iter().position(|entry| {
|
||||
matches!(entry, ListEntry::Thread(t) if &t.session_info.session_id == session_id)
|
||||
matches!(entry, ListEntry::Thread(t) if &t.metadata.session_id == session_id)
|
||||
});
|
||||
|
||||
// Find the workspace that owns this thread's project group by
|
||||
@@ -2358,10 +2296,7 @@ impl Sidebar {
|
||||
});
|
||||
|
||||
if let Some(next) = next_thread {
|
||||
let next_session_id = next.session_info.session_id.clone();
|
||||
let next_agent = next.agent.clone();
|
||||
let next_work_dirs = next.session_info.work_dirs.clone();
|
||||
let next_title = next.session_info.title.clone();
|
||||
let next_metadata = next.metadata.clone();
|
||||
// Use the thread's own workspace when it has one open (e.g. an absorbed
|
||||
// linked worktree thread that appears under the main workspace's header
|
||||
// but belongs to its own workspace). Loading into the wrong panel binds
|
||||
@@ -2371,17 +2306,17 @@ impl Sidebar {
|
||||
ThreadEntryWorkspace::Open(ws) => Some(ws.clone()),
|
||||
ThreadEntryWorkspace::Closed(_) => group_workspace,
|
||||
};
|
||||
self.focused_thread = Some(next_session_id.clone());
|
||||
self.record_thread_access(&next_session_id);
|
||||
self.focused_thread = Some(next_metadata.session_id.clone());
|
||||
self.record_thread_access(&next_metadata.session_id);
|
||||
|
||||
if let Some(workspace) = target_workspace {
|
||||
if let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) {
|
||||
agent_panel.update(cx, |panel, cx| {
|
||||
panel.load_agent_thread(
|
||||
next_agent,
|
||||
next_session_id,
|
||||
next_work_dirs,
|
||||
next_title,
|
||||
Agent::from(next_metadata.agent_id.clone()),
|
||||
next_metadata.session_id.clone(),
|
||||
Some(next_metadata.folder_paths.clone()),
|
||||
Some(next_metadata.title.clone()),
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
@@ -2419,7 +2354,7 @@ impl Sidebar {
|
||||
AgentThreadStatus::Completed | AgentThreadStatus::Error => {}
|
||||
}
|
||||
|
||||
let session_id = thread.session_info.session_id.clone();
|
||||
let session_id = thread.metadata.session_id.clone();
|
||||
self.archive_thread(&session_id, window, cx)
|
||||
}
|
||||
|
||||
@@ -2453,28 +2388,22 @@ impl Sidebar {
|
||||
};
|
||||
let notified = self
|
||||
.contents
|
||||
.is_thread_notified(&thread.session_info.session_id);
|
||||
let timestamp: SharedString = self
|
||||
.thread_last_message_sent_or_queued
|
||||
.get(&thread.session_info.session_id)
|
||||
.copied()
|
||||
.or(thread.session_info.created_at)
|
||||
.or(thread.session_info.updated_at)
|
||||
.map(format_history_entry_timestamp)
|
||||
.unwrap_or_default()
|
||||
.into();
|
||||
.is_thread_notified(&thread.metadata.session_id);
|
||||
let timestamp: SharedString = format_history_entry_timestamp(
|
||||
self.thread_last_message_sent_or_queued
|
||||
.get(&thread.metadata.session_id)
|
||||
.copied()
|
||||
.or(thread.metadata.created_at)
|
||||
.unwrap_or(thread.metadata.updated_at),
|
||||
)
|
||||
.into();
|
||||
Some(ThreadSwitcherEntry {
|
||||
session_id: thread.session_info.session_id.clone(),
|
||||
title: thread
|
||||
.session_info
|
||||
.title
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Untitled".into()),
|
||||
session_id: thread.metadata.session_id.clone(),
|
||||
title: thread.metadata.title.clone(),
|
||||
icon: thread.icon,
|
||||
icon_from_external_svg: thread.icon_from_external_svg.clone(),
|
||||
status: thread.status,
|
||||
agent: thread.agent.clone(),
|
||||
session_info: thread.session_info.clone(),
|
||||
metadata: thread.metadata.clone(),
|
||||
workspace,
|
||||
worktree_name: thread.worktrees.first().map(|wt| wt.name.clone()),
|
||||
|
||||
@@ -2505,8 +2434,8 @@ impl Sidebar {
|
||||
(Some(_), None) => std::cmp::Ordering::Less,
|
||||
(None, Some(_)) => std::cmp::Ordering::Greater,
|
||||
(None, None) => {
|
||||
let a_time = a.session_info.created_at.or(a.session_info.updated_at);
|
||||
let b_time = b.session_info.created_at.or(b.session_info.updated_at);
|
||||
let a_time = a.metadata.created_at.or(Some(a.metadata.updated_at));
|
||||
let b_time = b.metadata.created_at.or(Some(b.metadata.updated_at));
|
||||
b_time.cmp(&a_time)
|
||||
}
|
||||
}
|
||||
@@ -2560,16 +2489,11 @@ impl Sidebar {
|
||||
|
||||
let weak_multi_workspace = self.multi_workspace.clone();
|
||||
|
||||
let original_agent = self
|
||||
let original_metadata = self
|
||||
.focused_thread
|
||||
.as_ref()
|
||||
.and_then(|focused_id| entries.iter().find(|e| &e.session_id == focused_id))
|
||||
.map(|e| e.agent.clone());
|
||||
let original_session_info = self
|
||||
.focused_thread
|
||||
.as_ref()
|
||||
.and_then(|focused_id| entries.iter().find(|e| &e.session_id == focused_id))
|
||||
.map(|e| e.session_info.clone());
|
||||
.map(|e| e.metadata.clone());
|
||||
let original_workspace = self
|
||||
.multi_workspace
|
||||
.upgrade()
|
||||
@@ -2583,8 +2507,7 @@ impl Sidebar {
|
||||
let thread_switcher = thread_switcher.clone();
|
||||
move |this, _emitter, event: &ThreadSwitcherEvent, window, cx| match event {
|
||||
ThreadSwitcherEvent::Preview {
|
||||
agent,
|
||||
session_info,
|
||||
metadata,
|
||||
workspace,
|
||||
} => {
|
||||
if let Some(mw) = weak_multi_workspace.upgrade() {
|
||||
@@ -2592,22 +2515,14 @@ impl Sidebar {
|
||||
mw.activate(workspace.clone(), cx);
|
||||
});
|
||||
}
|
||||
this.focused_thread = Some(session_info.session_id.clone());
|
||||
this.focused_thread = Some(metadata.session_id.clone());
|
||||
this.update_entries(cx);
|
||||
Self::load_agent_thread_in_workspace(
|
||||
workspace,
|
||||
agent.clone(),
|
||||
session_info.clone(),
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
Self::load_agent_thread_in_workspace(workspace, metadata, false, window, cx);
|
||||
let focus = thread_switcher.focus_handle(cx);
|
||||
window.focus(&focus, cx);
|
||||
}
|
||||
ThreadSwitcherEvent::Confirmed {
|
||||
agent,
|
||||
session_info,
|
||||
metadata,
|
||||
workspace,
|
||||
} => {
|
||||
if let Some(mw) = weak_multi_workspace.upgrade() {
|
||||
@@ -2615,17 +2530,10 @@ impl Sidebar {
|
||||
mw.activate(workspace.clone(), cx);
|
||||
});
|
||||
}
|
||||
this.record_thread_access(&session_info.session_id);
|
||||
this.focused_thread = Some(session_info.session_id.clone());
|
||||
this.record_thread_access(&metadata.session_id);
|
||||
this.focused_thread = Some(metadata.session_id.clone());
|
||||
this.update_entries(cx);
|
||||
Self::load_agent_thread_in_workspace(
|
||||
workspace,
|
||||
agent.clone(),
|
||||
session_info.clone(),
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
Self::load_agent_thread_in_workspace(workspace, metadata, false, window, cx);
|
||||
this.dismiss_thread_switcher(cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.focus_panel::<AgentPanel>(window, cx);
|
||||
@@ -2639,15 +2547,13 @@ impl Sidebar {
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(session_info) = &original_session_info {
|
||||
this.focused_thread = Some(session_info.session_id.clone());
|
||||
if let Some(metadata) = &original_metadata {
|
||||
this.focused_thread = Some(metadata.session_id.clone());
|
||||
this.update_entries(cx);
|
||||
let agent = original_agent.clone().unwrap_or(Agent::NativeAgent);
|
||||
if let Some(original_ws) = &original_workspace {
|
||||
Self::load_agent_thread_in_workspace(
|
||||
original_ws,
|
||||
agent,
|
||||
session_info.clone(),
|
||||
metadata,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
@@ -2672,13 +2578,10 @@ impl Sidebar {
|
||||
|
||||
// Replay the initial preview that was emitted during construction
|
||||
// before subscriptions were wired up.
|
||||
let initial_preview = thread_switcher.read(cx).selected_entry().map(|entry| {
|
||||
(
|
||||
entry.agent.clone(),
|
||||
entry.session_info.clone(),
|
||||
entry.workspace.clone(),
|
||||
)
|
||||
});
|
||||
let initial_preview = thread_switcher
|
||||
.read(cx)
|
||||
.selected_entry()
|
||||
.map(|entry| (entry.metadata.clone(), entry.workspace.clone()));
|
||||
|
||||
self.thread_switcher = Some(thread_switcher);
|
||||
self._thread_switcher_subscriptions = subscriptions;
|
||||
@@ -2688,22 +2591,15 @@ impl Sidebar {
|
||||
});
|
||||
}
|
||||
|
||||
if let Some((agent, session_info, workspace)) = initial_preview {
|
||||
if let Some((metadata, workspace)) = initial_preview {
|
||||
if let Some(mw) = self.multi_workspace.upgrade() {
|
||||
mw.update(cx, |mw, cx| {
|
||||
mw.activate(workspace.clone(), cx);
|
||||
});
|
||||
}
|
||||
self.focused_thread = Some(session_info.session_id.clone());
|
||||
self.focused_thread = Some(metadata.session_id.clone());
|
||||
self.update_entries(cx);
|
||||
Self::load_agent_thread_in_workspace(
|
||||
&workspace,
|
||||
agent,
|
||||
session_info,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
Self::load_agent_thread_in_workspace(&workspace, &metadata, false, window, cx);
|
||||
}
|
||||
|
||||
window.focus(&focus, cx);
|
||||
@@ -2718,36 +2614,32 @@ impl Sidebar {
|
||||
) -> AnyElement {
|
||||
let has_notification = self
|
||||
.contents
|
||||
.is_thread_notified(&thread.session_info.session_id);
|
||||
.is_thread_notified(&thread.metadata.session_id);
|
||||
|
||||
let title: SharedString = thread
|
||||
.session_info
|
||||
.title
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Untitled".into());
|
||||
let session_info = thread.session_info.clone();
|
||||
let title: SharedString = thread.metadata.title.clone();
|
||||
let metadata = thread.metadata.clone();
|
||||
let thread_workspace = thread.workspace.clone();
|
||||
|
||||
let is_hovered = self.hovered_thread_index == Some(ix);
|
||||
let is_selected = self.agent_panel_visible
|
||||
&& self.focused_thread.as_ref() == Some(&session_info.session_id);
|
||||
let is_selected =
|
||||
self.agent_panel_visible && self.focused_thread.as_ref() == Some(&metadata.session_id);
|
||||
let is_running = matches!(
|
||||
thread.status,
|
||||
AgentThreadStatus::Running | AgentThreadStatus::WaitingForConfirmation
|
||||
);
|
||||
|
||||
let session_id_for_delete = thread.session_info.session_id.clone();
|
||||
let session_id_for_delete = thread.metadata.session_id.clone();
|
||||
let focus_handle = self.focus_handle.clone();
|
||||
|
||||
let id = SharedString::from(format!("thread-entry-{}", ix));
|
||||
|
||||
let timestamp = self
|
||||
.thread_last_message_sent_or_queued
|
||||
.get(&thread.session_info.session_id)
|
||||
.copied()
|
||||
.or(thread.session_info.created_at)
|
||||
.or(thread.session_info.updated_at)
|
||||
.map(format_history_entry_timestamp);
|
||||
let timestamp = format_history_entry_timestamp(
|
||||
self.thread_last_message_sent_or_queued
|
||||
.get(&thread.metadata.session_id)
|
||||
.copied()
|
||||
.or(thread.metadata.created_at)
|
||||
.unwrap_or(thread.metadata.updated_at),
|
||||
);
|
||||
|
||||
ThreadItem::new(id, title)
|
||||
.icon(thread.icon)
|
||||
@@ -2766,7 +2658,7 @@ impl Sidebar {
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.when_some(timestamp, |this, ts| this.timestamp(ts))
|
||||
.timestamp(timestamp)
|
||||
.highlight_positions(thread.highlight_positions.to_vec())
|
||||
.title_generating(thread.is_title_generating)
|
||||
.notified(has_notification)
|
||||
@@ -2827,23 +2719,15 @@ impl Sidebar {
|
||||
)
|
||||
})
|
||||
.on_click({
|
||||
let agent = thread.agent.clone();
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
this.selection = None;
|
||||
match &thread_workspace {
|
||||
ThreadEntryWorkspace::Open(workspace) => {
|
||||
this.activate_thread(
|
||||
agent.clone(),
|
||||
session_info.clone(),
|
||||
workspace,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
this.activate_thread(metadata.clone(), workspace, window, cx);
|
||||
}
|
||||
ThreadEntryWorkspace::Closed(path_list) => {
|
||||
this.open_workspace_and_activate_thread(
|
||||
agent.clone(),
|
||||
session_info.clone(),
|
||||
metadata.clone(),
|
||||
path_list.clone(),
|
||||
window,
|
||||
cx,
|
||||
@@ -3333,18 +3217,7 @@ impl Sidebar {
|
||||
}
|
||||
ThreadsArchiveViewEvent::Unarchive { thread } => {
|
||||
this.show_thread_list(window, cx);
|
||||
|
||||
let agent = Agent::from(thread.agent_id.clone());
|
||||
let session_info = acp_thread::AgentSessionInfo {
|
||||
session_id: thread.session_id.clone(),
|
||||
work_dirs: Some(thread.folder_paths.clone()),
|
||||
title: Some(thread.title.clone()),
|
||||
updated_at: Some(thread.updated_at),
|
||||
created_at: thread.created_at,
|
||||
meta: None,
|
||||
};
|
||||
|
||||
this.activate_archived_thread(agent, session_info, window, cx);
|
||||
this.activate_archived_thread(thread.clone(), window, cx);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ use feature_flags::FeatureFlagAppExt as _;
|
||||
use fs::FakeFs;
|
||||
use gpui::TestAppContext;
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::AgentId;
|
||||
use settings::SettingsStore;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use util::path_list::PathList;
|
||||
@@ -30,9 +31,11 @@ fn init_test(cx: &mut TestAppContext) {
|
||||
}
|
||||
|
||||
fn has_thread_entry(sidebar: &Sidebar, session_id: &acp::SessionId) -> bool {
|
||||
sidebar.contents.entries.iter().any(
|
||||
|entry| matches!(entry, ListEntry::Thread(t) if &t.session_info.session_id == session_id),
|
||||
)
|
||||
sidebar
|
||||
.contents
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| matches!(entry, ListEntry::Thread(t) if &t.metadata.session_id == session_id))
|
||||
}
|
||||
|
||||
async fn init_test_project(
|
||||
@@ -176,12 +179,7 @@ fn visible_entries_as_strings(
|
||||
format!("{} [{}]{}", icon, label, selected)
|
||||
}
|
||||
ListEntry::Thread(thread) => {
|
||||
let title = thread
|
||||
.session_info
|
||||
.title
|
||||
.as_ref()
|
||||
.map(|s| s.as_ref())
|
||||
.unwrap_or("Untitled");
|
||||
let title = thread.metadata.title.as_ref();
|
||||
let active = if thread.is_live { " *" } else { "" };
|
||||
let status_str = match thread.status {
|
||||
AgentThreadStatus::Running => " (running)",
|
||||
@@ -191,7 +189,7 @@ fn visible_entries_as_strings(
|
||||
};
|
||||
let notified = if sidebar
|
||||
.contents
|
||||
.is_thread_notified(&thread.session_info.session_id)
|
||||
.is_thread_notified(&thread.metadata.session_id)
|
||||
{
|
||||
" (!)"
|
||||
} else {
|
||||
@@ -565,14 +563,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
is_active: true,
|
||||
},
|
||||
ListEntry::Thread(ThreadEntry {
|
||||
agent: Agent::NativeAgent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
metadata: ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("t-1")),
|
||||
work_dirs: None,
|
||||
title: Some("Completed thread".into()),
|
||||
updated_at: Some(Utc::now()),
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
folder_paths: PathList::default(),
|
||||
title: "Completed thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
meta: None,
|
||||
archived: false,
|
||||
},
|
||||
icon: IconName::ZedAgent,
|
||||
icon_from_external_svg: None,
|
||||
@@ -587,14 +585,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
}),
|
||||
// Active thread with Running status
|
||||
ListEntry::Thread(ThreadEntry {
|
||||
agent: Agent::NativeAgent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
metadata: ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("t-2")),
|
||||
work_dirs: None,
|
||||
title: Some("Running thread".into()),
|
||||
updated_at: Some(Utc::now()),
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
folder_paths: PathList::default(),
|
||||
title: "Running thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
meta: None,
|
||||
archived: false,
|
||||
},
|
||||
icon: IconName::ZedAgent,
|
||||
icon_from_external_svg: None,
|
||||
@@ -609,14 +607,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
}),
|
||||
// Active thread with Error status
|
||||
ListEntry::Thread(ThreadEntry {
|
||||
agent: Agent::NativeAgent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
metadata: ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("t-3")),
|
||||
work_dirs: None,
|
||||
title: Some("Error thread".into()),
|
||||
updated_at: Some(Utc::now()),
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
folder_paths: PathList::default(),
|
||||
title: "Error thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
meta: None,
|
||||
archived: false,
|
||||
},
|
||||
icon: IconName::ZedAgent,
|
||||
icon_from_external_svg: None,
|
||||
@@ -631,14 +629,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
}),
|
||||
// Thread with WaitingForConfirmation status, not active
|
||||
ListEntry::Thread(ThreadEntry {
|
||||
agent: Agent::NativeAgent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
metadata: ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("t-4")),
|
||||
work_dirs: None,
|
||||
title: Some("Waiting thread".into()),
|
||||
updated_at: Some(Utc::now()),
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
folder_paths: PathList::default(),
|
||||
title: "Waiting thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
meta: None,
|
||||
archived: false,
|
||||
},
|
||||
icon: IconName::ZedAgent,
|
||||
icon_from_external_svg: None,
|
||||
@@ -653,14 +651,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
}),
|
||||
// Background thread that completed (should show notification)
|
||||
ListEntry::Thread(ThreadEntry {
|
||||
agent: Agent::NativeAgent,
|
||||
session_info: acp_thread::AgentSessionInfo {
|
||||
metadata: ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("t-5")),
|
||||
work_dirs: None,
|
||||
title: Some("Notified thread".into()),
|
||||
updated_at: Some(Utc::now()),
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
folder_paths: PathList::default(),
|
||||
title: "Notified thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
meta: None,
|
||||
archived: false,
|
||||
},
|
||||
icon: IconName::ZedAgent,
|
||||
icon_from_external_svg: None,
|
||||
@@ -1919,14 +1917,14 @@ async fn test_focused_thread_tracks_user_intent(cx: &mut TestAppContext) {
|
||||
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.activate_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id_a.clone(),
|
||||
work_dirs: None,
|
||||
title: Some("Test".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Test".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::default(),
|
||||
archived: false,
|
||||
},
|
||||
&workspace_a,
|
||||
window,
|
||||
@@ -1974,14 +1972,14 @@ async fn test_focused_thread_tracks_user_intent(cx: &mut TestAppContext) {
|
||||
// which also triggers a workspace switch.
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.activate_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id_b.clone(),
|
||||
work_dirs: None,
|
||||
title: Some("Thread B".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Thread B".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::default(),
|
||||
archived: false,
|
||||
},
|
||||
&workspace_b,
|
||||
window,
|
||||
@@ -3231,24 +3229,14 @@ async fn test_clicking_worktree_thread_does_not_briefly_render_as_separate_proje
|
||||
);
|
||||
}
|
||||
ListEntry::Thread(thread)
|
||||
if thread
|
||||
.session_info
|
||||
.title
|
||||
.as_ref()
|
||||
.map(|title| title.as_ref())
|
||||
== Some("WT Thread")
|
||||
if thread.metadata.title.as_ref() == "WT Thread"
|
||||
&& thread.worktrees.first().map(|wt| wt.name.as_ref())
|
||||
== Some("wt-feature-a") =>
|
||||
{
|
||||
saw_expected_thread = true;
|
||||
}
|
||||
ListEntry::Thread(thread) => {
|
||||
let title = thread
|
||||
.session_info
|
||||
.title
|
||||
.as_ref()
|
||||
.map(|title| title.as_ref())
|
||||
.unwrap_or("Untitled");
|
||||
let title = thread.metadata.title.as_ref();
|
||||
let worktree_name = thread
|
||||
.worktrees
|
||||
.first()
|
||||
@@ -3446,14 +3434,14 @@ async fn test_activate_archived_thread_with_saved_paths_activates_matching_works
|
||||
// switch to the workspace for project-b.
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id.clone(),
|
||||
work_dirs: Some(PathList::new(&[PathBuf::from("/project-b")])),
|
||||
title: Some("Archived Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Archived Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::new(&[PathBuf::from("/project-b")]),
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -3507,14 +3495,14 @@ async fn test_activate_archived_thread_cwd_fallback_with_matching_workspace(
|
||||
// No thread saved to the store – cwd is the only path hint.
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("unknown-session")),
|
||||
work_dirs: Some(PathList::new(&[std::path::PathBuf::from("/project-b")])),
|
||||
title: Some("CWD Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "CWD Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::new(&[std::path::PathBuf::from("/project-b")]),
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -3568,14 +3556,14 @@ async fn test_activate_archived_thread_no_paths_no_cwd_uses_active_workspace(
|
||||
// No saved thread, no cwd – should fall back to the active workspace.
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: acp::SessionId::new(Arc::from("no-context-session")),
|
||||
work_dirs: None,
|
||||
title: Some("Contextless Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Contextless Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::default(),
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -3622,14 +3610,14 @@ async fn test_activate_archived_thread_saved_paths_opens_new_workspace(cx: &mut
|
||||
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id.clone(),
|
||||
work_dirs: Some(path_list_b),
|
||||
title: Some("New WS Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "New WS Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: path_list_b,
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -3671,14 +3659,14 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window(cx: &m
|
||||
|
||||
sidebar.update_in(cx_a, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id.clone(),
|
||||
work_dirs: Some(PathList::new(&[PathBuf::from("/project-b")])),
|
||||
title: Some("Cross Window Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Cross Window Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::new(&[PathBuf::from("/project-b")]),
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -3747,14 +3735,14 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window_with_t
|
||||
|
||||
sidebar_a.update_in(cx_a, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id.clone(),
|
||||
work_dirs: Some(PathList::new(&[PathBuf::from("/project-b")])),
|
||||
title: Some("Cross Window Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Cross Window Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::new(&[PathBuf::from("/project-b")]),
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
@@ -3822,14 +3810,14 @@ async fn test_activate_archived_thread_prefers_current_window_for_matching_paths
|
||||
|
||||
sidebar_a.update_in(cx_a, |sidebar, window, cx| {
|
||||
sidebar.activate_archived_thread(
|
||||
Agent::NativeAgent,
|
||||
acp_thread::AgentSessionInfo {
|
||||
ThreadMetadata {
|
||||
session_id: session_id.clone(),
|
||||
work_dirs: Some(PathList::new(&[PathBuf::from("/project-a")])),
|
||||
title: Some("Current Window Thread".into()),
|
||||
updated_at: None,
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: "Current Window Thread".into(),
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
meta: None,
|
||||
folder_paths: PathList::new(&[PathBuf::from("/project-a")]),
|
||||
archived: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use acp_thread;
|
||||
use action_log::DiffStats;
|
||||
use agent_client_protocol as acp;
|
||||
use agent_ui::Agent;
|
||||
use agent_ui::thread_metadata_store::ThreadMetadata;
|
||||
use gpui::{
|
||||
Action as _, Animation, AnimationExt, AnyElement, DismissEvent, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, Hsla, Modifiers, ModifiersChangedEvent, Render, SharedString,
|
||||
@@ -23,8 +22,7 @@ pub(crate) struct ThreadSwitcherEntry {
|
||||
pub icon: IconName,
|
||||
pub icon_from_external_svg: Option<SharedString>,
|
||||
pub status: AgentThreadStatus,
|
||||
pub agent: Agent,
|
||||
pub session_info: acp_thread::AgentSessionInfo,
|
||||
pub metadata: ThreadMetadata,
|
||||
pub workspace: Entity<Workspace>,
|
||||
pub worktree_name: Option<SharedString>,
|
||||
pub diff_stats: DiffStats,
|
||||
@@ -35,13 +33,11 @@ pub(crate) struct ThreadSwitcherEntry {
|
||||
|
||||
pub(crate) enum ThreadSwitcherEvent {
|
||||
Preview {
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
workspace: Entity<Workspace>,
|
||||
},
|
||||
Confirmed {
|
||||
agent: Agent,
|
||||
session_info: acp_thread::AgentSessionInfo,
|
||||
metadata: ThreadMetadata,
|
||||
workspace: Entity<Workspace>,
|
||||
},
|
||||
Dismissed,
|
||||
@@ -72,8 +68,7 @@ impl ThreadSwitcher {
|
||||
|
||||
if let Some(entry) = entries.get(selected_index) {
|
||||
cx.emit(ThreadSwitcherEvent::Preview {
|
||||
agent: entry.agent.clone(),
|
||||
session_info: entry.session_info.clone(),
|
||||
metadata: entry.metadata.clone(),
|
||||
workspace: entry.workspace.clone(),
|
||||
});
|
||||
}
|
||||
@@ -130,8 +125,7 @@ impl ThreadSwitcher {
|
||||
fn emit_preview(&mut self, cx: &mut Context<Self>) {
|
||||
if let Some(entry) = self.entries.get(self.selected_index) {
|
||||
cx.emit(ThreadSwitcherEvent::Preview {
|
||||
agent: entry.agent.clone(),
|
||||
session_info: entry.session_info.clone(),
|
||||
metadata: entry.metadata.clone(),
|
||||
workspace: entry.workspace.clone(),
|
||||
});
|
||||
}
|
||||
@@ -140,8 +134,7 @@ impl ThreadSwitcher {
|
||||
fn confirm(&mut self, _: &menu::Confirm, _window: &mut gpui::Window, cx: &mut Context<Self>) {
|
||||
if let Some(entry) = self.entries.get(self.selected_index) {
|
||||
cx.emit(ThreadSwitcherEvent::Confirmed {
|
||||
agent: entry.agent.clone(),
|
||||
session_info: entry.session_info.clone(),
|
||||
metadata: entry.metadata.clone(),
|
||||
workspace: entry.workspace.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user