agent_ui: Only create ThreadHistory if agent supports it (#51759)
Rather than always having a ThreadHistory with an internal option, changed it to be an Option<ThreadHistory> so we can better distingiuish between an empty list and unsupported Release Notes: - N/A
This commit is contained in:
1 parent
95c96ad9a1
commit
ef40f2144d
12 files changed
+197
-244
No files matched your search
@@ -24,7 +24,7 @@ pub enum AgentConnectionEntry {
|
||||
#[derive(Clone)]
|
||||
pub struct AgentConnectedState {
|
||||
pub connection: Rc<dyn AgentConnection>,
|
||||
pub history: Entity<ThreadHistory>,
|
||||
pub history: Option<Entity<ThreadHistory>>,
|
||||
}
|
||||
|
||||
impl AgentConnectionEntry {
|
||||
@@ -38,7 +38,7 @@ impl AgentConnectionEntry {
|
||||
|
||||
pub fn history(&self) -> Option<&Entity<ThreadHistory>> {
|
||||
match self {
|
||||
AgentConnectionEntry::Connected(state) => Some(&state.history),
|
||||
AgentConnectionEntry::Connected(state) => state.history.as_ref(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,9 @@ impl AgentConnectionStore {
|
||||
let connect_task = server.connect(delegate, cx);
|
||||
let connect_task = cx.spawn(async move |_this, cx| match connect_task.await {
|
||||
Ok(connection) => cx.update(|cx| {
|
||||
let history = cx.new(|cx| ThreadHistory::new(connection.session_list(cx), cx));
|
||||
let history = connection
|
||||
.session_list(cx)
|
||||
.map(|session_list| cx.new(|cx| ThreadHistory::new(session_list, cx)));
|
||||
Ok(AgentConnectedState {
|
||||
connection,
|
||||
history,
|
||||
|
||||
@@ -1461,13 +1461,9 @@ impl AgentPanel {
|
||||
.read(cx)
|
||||
.history()?
|
||||
.clone();
|
||||
if history.read(cx).has_session_list() {
|
||||
Some(History::AgentThreads {
|
||||
view: self.create_thread_history_view(agent, history, window, cx),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Some(History::AgentThreads {
|
||||
view: self.create_thread_history_view(agent, history, window, cx),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4650,16 +4646,13 @@ impl rules_library::InlineAssistDelegate for PromptLibraryInlineAssist {
|
||||
let Some(panel) = workspace.read(cx).panel::<AgentPanel>(cx) else {
|
||||
return;
|
||||
};
|
||||
let Some(history) = panel
|
||||
let history = panel
|
||||
.read(cx)
|
||||
.connection_store()
|
||||
.read(cx)
|
||||
.entry(&crate::Agent::NativeAgent)
|
||||
.and_then(|s| s.read(cx).history())
|
||||
else {
|
||||
log::error!("No connection entry found for native agent");
|
||||
return;
|
||||
};
|
||||
.map(|h| h.downgrade());
|
||||
let project = workspace.read(cx).project().downgrade();
|
||||
let panel = panel.read(cx);
|
||||
let thread_store = panel.thread_store().clone();
|
||||
@@ -4669,7 +4662,7 @@ impl rules_library::InlineAssistDelegate for PromptLibraryInlineAssist {
|
||||
project,
|
||||
thread_store,
|
||||
None,
|
||||
history.downgrade(),
|
||||
history,
|
||||
initial_prompt,
|
||||
window,
|
||||
cx,
|
||||
|
||||
@@ -223,7 +223,7 @@ pub struct PromptCompletionProvider<T: PromptCompletionProviderDelegate> {
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: Entity<MentionSet>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
}
|
||||
@@ -233,7 +233,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
source: T,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: Entity<MentionSet>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
) -> Self {
|
||||
@@ -920,7 +920,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
}
|
||||
|
||||
Some(PromptContextType::Thread) => {
|
||||
if let Some(history) = self.history.upgrade() {
|
||||
if let Some(history) = self.history.as_ref().and_then(|h| h.upgrade()) {
|
||||
let sessions = history
|
||||
.read(cx)
|
||||
.sessions()
|
||||
@@ -1146,7 +1146,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
return Task::ready(recent);
|
||||
}
|
||||
|
||||
if let Some(history) = self.history.upgrade() {
|
||||
if let Some(history) = self.history.as_ref().and_then(|h| h.upgrade()) {
|
||||
const RECENT_COUNT: usize = 2;
|
||||
recent.extend(
|
||||
history
|
||||
|
||||
@@ -421,7 +421,7 @@ pub struct ConnectedServerState {
|
||||
active_id: Option<acp::SessionId>,
|
||||
threads: HashMap<acp::SessionId, Entity<ThreadView>>,
|
||||
connection: Rc<dyn AgentConnection>,
|
||||
history: Entity<ThreadHistory>,
|
||||
history: Option<Entity<ThreadHistory>>,
|
||||
conversation: Entity<Conversation>,
|
||||
_connection_entry_subscription: Subscription,
|
||||
}
|
||||
@@ -816,7 +816,7 @@ impl ConversationView {
|
||||
conversation: Entity<Conversation>,
|
||||
resumed_without_history: bool,
|
||||
initial_content: Option<AgentInitialContent>,
|
||||
history: Entity<ThreadHistory>,
|
||||
history: Option<Entity<ThreadHistory>>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Entity<ThreadView> {
|
||||
@@ -833,7 +833,7 @@ impl ConversationView {
|
||||
self.workspace.clone(),
|
||||
self.project.downgrade(),
|
||||
self.thread_store.clone(),
|
||||
history.downgrade(),
|
||||
history.as_ref().map(|h| h.downgrade()),
|
||||
self.prompt_store.clone(),
|
||||
prompt_capabilities.clone(),
|
||||
available_commands.clone(),
|
||||
@@ -1082,7 +1082,7 @@ impl ConversationView {
|
||||
threads: HashMap::default(),
|
||||
connection,
|
||||
conversation: cx.new(|_cx| Conversation::default()),
|
||||
history: cx.new(|cx| ThreadHistory::new(None, cx)),
|
||||
history: None,
|
||||
_connection_entry_subscription: Subscription::new(|| {}),
|
||||
}),
|
||||
cx,
|
||||
@@ -2213,7 +2213,7 @@ impl ConversationView {
|
||||
let Some(connected) = self.as_connected() else {
|
||||
return;
|
||||
};
|
||||
let history = connected.history.downgrade();
|
||||
let history = connected.history.as_ref().map(|h| h.downgrade());
|
||||
let Some(thread) = connected.active_view() else {
|
||||
return;
|
||||
};
|
||||
@@ -2601,7 +2601,7 @@ impl ConversationView {
|
||||
}
|
||||
|
||||
pub fn history(&self) -> Option<&Entity<ThreadHistory>> {
|
||||
self.as_connected().map(|c| &c.history)
|
||||
self.as_connected().and_then(|c| c.history.as_ref())
|
||||
}
|
||||
|
||||
pub fn delete_history_entry(&mut self, session_id: &acp::SessionId, cx: &mut Context<Self>) {
|
||||
@@ -2609,9 +2609,10 @@ impl ConversationView {
|
||||
return;
|
||||
};
|
||||
|
||||
let task = connected
|
||||
.history
|
||||
.update(cx, |history, cx| history.delete_session(&session_id, cx));
|
||||
let Some(history) = &connected.history else {
|
||||
return;
|
||||
};
|
||||
let task = history.update(cx, |history, cx| history.delete_session(&session_id, cx));
|
||||
task.detach_and_log_err(cx);
|
||||
|
||||
if let Some(store) = ThreadMetadataStore::try_global(cx) {
|
||||
@@ -2902,60 +2903,14 @@ pub(crate) mod tests {
|
||||
let session_a = AgentSessionInfo::new(SessionId::new("session-a"));
|
||||
let session_b = AgentSessionInfo::new(SessionId::new("session-b"));
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
let conversation_view = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
ConversationView::new(
|
||||
Rc::new(StubAgentServer::default_response()),
|
||||
connection_store,
|
||||
Agent::Custom { id: "Test".into() },
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
workspace.downgrade(),
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
// Wait for connection to establish
|
||||
cx.run_until_parked();
|
||||
|
||||
let history = cx.update(|_window, cx| {
|
||||
conversation_view
|
||||
.read(cx)
|
||||
.history()
|
||||
.expect("Missing history")
|
||||
.clone()
|
||||
});
|
||||
|
||||
// Initially empty because StubAgentConnection.session_list() returns None
|
||||
active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
|
||||
assert_eq!(view.recent_history_entries.len(), 0);
|
||||
});
|
||||
|
||||
// Now set the session list - this simulates external agents providing their history
|
||||
let list_a: Rc<dyn AgentSessionList> =
|
||||
Rc::new(StubSessionList::new(vec![session_a.clone()]));
|
||||
history.update(cx, |history, cx| {
|
||||
history.set_session_list(Some(list_a), cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
// Use a connection that provides a session list so ThreadHistory is created
|
||||
let (conversation_view, history, cx) = setup_thread_view_with_history(
|
||||
StubAgentServer::new(SessionHistoryConnection::new(vec![session_a.clone()])),
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Initially has session_a from the connection's session list
|
||||
active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
|
||||
assert_eq!(view.recent_history_entries.len(), 1);
|
||||
assert_eq!(
|
||||
@@ -2964,11 +2919,11 @@ pub(crate) mod tests {
|
||||
);
|
||||
});
|
||||
|
||||
// Update to a different session list
|
||||
// Swap to a different session list
|
||||
let list_b: Rc<dyn AgentSessionList> =
|
||||
Rc::new(StubSessionList::new(vec![session_b.clone()]));
|
||||
history.update(cx, |history, cx| {
|
||||
history.set_session_list(Some(list_b), cx);
|
||||
history.set_session_list(list_b, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
@@ -2986,19 +2941,12 @@ pub(crate) mod tests {
|
||||
init_test(cx);
|
||||
|
||||
let session = AgentSessionInfo::new(SessionId::new("history-session"));
|
||||
let (conversation_view, history, cx) = setup_thread_view_with_history(
|
||||
let (conversation_view, _history, cx) = setup_thread_view_with_history(
|
||||
StubAgentServer::new(SessionHistoryConnection::new(vec![session.clone()])),
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
history.read_with(cx, |history, _cx| {
|
||||
assert!(
|
||||
history.has_session_list(),
|
||||
"session list should be attached after thread creation"
|
||||
);
|
||||
});
|
||||
|
||||
active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
|
||||
assert_eq!(view.recent_history_entries.len(), 1);
|
||||
assert_eq!(
|
||||
|
||||
@@ -228,8 +228,8 @@ pub struct ThreadView {
|
||||
pub hovered_recent_history_item: Option<usize>,
|
||||
pub show_external_source_prompt_warning: bool,
|
||||
pub show_codex_windows_warning: bool,
|
||||
pub history: Entity<ThreadHistory>,
|
||||
pub _history_subscription: Subscription,
|
||||
pub history: Option<Entity<ThreadHistory>>,
|
||||
pub _history_subscription: Option<Subscription>,
|
||||
}
|
||||
impl Focusable for ThreadView {
|
||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||
@@ -273,7 +273,7 @@ impl ThreadView {
|
||||
resumed_without_history: bool,
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
history: Entity<ThreadHistory>,
|
||||
history: Option<Entity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
initial_content: Option<AgentInitialContent>,
|
||||
mut subscriptions: Vec<Subscription>,
|
||||
@@ -284,8 +284,10 @@ impl ThreadView {
|
||||
|
||||
let placeholder = placeholder_text(agent_display_name.as_ref(), false);
|
||||
|
||||
let history_subscription = cx.observe(&history, |this, history, cx| {
|
||||
this.update_recent_history_from_cache(&history, cx);
|
||||
let history_subscription = history.as_ref().map(|h| {
|
||||
cx.observe(h, |this, history, cx| {
|
||||
this.update_recent_history_from_cache(&history, cx);
|
||||
})
|
||||
});
|
||||
|
||||
let mut should_auto_submit = false;
|
||||
@@ -296,7 +298,7 @@ impl ThreadView {
|
||||
workspace.clone(),
|
||||
project.clone(),
|
||||
thread_store,
|
||||
history.downgrade(),
|
||||
history.as_ref().map(|h| h.downgrade()),
|
||||
prompt_store,
|
||||
prompt_capabilities.clone(),
|
||||
available_commands.clone(),
|
||||
@@ -392,7 +394,10 @@ impl ThreadView {
|
||||
}));
|
||||
}));
|
||||
|
||||
let recent_history_entries = history.read(cx).get_recent_sessions(3);
|
||||
let recent_history_entries = history
|
||||
.as_ref()
|
||||
.map(|h| h.read(cx).get_recent_sessions(3))
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut this = Self {
|
||||
id,
|
||||
@@ -7502,7 +7507,10 @@ impl ThreadView {
|
||||
),
|
||||
)
|
||||
.child(v_flex().p_1().pr_1p5().gap_1().children({
|
||||
let supports_delete = self.history.read(cx).supports_delete();
|
||||
let supports_delete = self
|
||||
.history
|
||||
.as_ref()
|
||||
.map_or(false, |h| h.read(cx).supports_delete());
|
||||
recent_history
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
|
||||
@@ -26,7 +26,7 @@ pub struct EntryViewState {
|
||||
workspace: WeakEntity<Workspace>,
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
entries: Vec<Entry>,
|
||||
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
|
||||
@@ -39,7 +39,7 @@ impl EntryViewState {
|
||||
workspace: WeakEntity<Workspace>,
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
|
||||
available_commands: Rc<RefCell<Vec<acp::AvailableCommand>>>,
|
||||
@@ -510,14 +510,14 @@ mod tests {
|
||||
});
|
||||
|
||||
let thread_store = None;
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
let history: Option<gpui::WeakEntity<crate::ThreadHistory>> = None;
|
||||
|
||||
let view_state = cx.new(|_cx| {
|
||||
EntryViewState::new(
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store,
|
||||
history.downgrade(),
|
||||
history,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
|
||||
@@ -278,15 +278,11 @@ impl InlineAssistant {
|
||||
|
||||
let prompt_store = agent_panel.prompt_store().as_ref().cloned();
|
||||
let thread_store = agent_panel.thread_store().clone();
|
||||
let Some(history) = agent_panel
|
||||
let history = agent_panel
|
||||
.connection_store()
|
||||
.read(cx)
|
||||
.entry(&crate::Agent::NativeAgent)
|
||||
.and_then(|s| s.read(cx).history().cloned())
|
||||
else {
|
||||
log::error!("No connection entry found for native agent");
|
||||
return;
|
||||
};
|
||||
.and_then(|s| s.read(cx).history().cloned());
|
||||
|
||||
let handle_assist =
|
||||
|window: &mut Window, cx: &mut Context<Workspace>| match inline_assist_target {
|
||||
@@ -298,7 +294,7 @@ impl InlineAssistant {
|
||||
workspace.project().downgrade(),
|
||||
thread_store,
|
||||
prompt_store,
|
||||
history.downgrade(),
|
||||
history.as_ref().map(|h| h.downgrade()),
|
||||
action.prompt.clone(),
|
||||
window,
|
||||
cx,
|
||||
@@ -313,7 +309,7 @@ impl InlineAssistant {
|
||||
workspace.project().downgrade(),
|
||||
thread_store,
|
||||
prompt_store,
|
||||
history.downgrade(),
|
||||
history.as_ref().map(|h| h.downgrade()),
|
||||
action.prompt.clone(),
|
||||
window,
|
||||
cx,
|
||||
@@ -495,7 +491,7 @@ impl InlineAssistant {
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
initial_prompt: Option<String>,
|
||||
window: &mut Window,
|
||||
codegen_ranges: &[Range<Anchor>],
|
||||
@@ -634,7 +630,7 @@ impl InlineAssistant {
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
initial_prompt: Option<String>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
@@ -679,7 +675,7 @@ impl InlineAssistant {
|
||||
workspace: Entity<Workspace>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> InlineAssistId {
|
||||
@@ -1983,8 +1979,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
|
||||
.read(cx)
|
||||
.entry(&crate::Agent::NativeAgent)
|
||||
.and_then(|e| e.read(cx).history())
|
||||
.context("no history found for native agent")?
|
||||
.downgrade();
|
||||
.map(|h| h.downgrade());
|
||||
|
||||
anyhow::Ok((panel.thread_store().clone(), history))
|
||||
})??;
|
||||
@@ -2155,7 +2150,7 @@ pub mod test {
|
||||
|
||||
setup(cx);
|
||||
|
||||
let (_editor, buffer, _history) = cx.update(|window, cx| {
|
||||
let (_editor, buffer) = cx.update(|window, cx| {
|
||||
let buffer = cx.new(|cx| Buffer::local("", cx));
|
||||
let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
|
||||
let editor = cx.new(|cx| Editor::for_multibuffer(multibuffer, None, window, cx));
|
||||
@@ -2172,7 +2167,6 @@ pub mod test {
|
||||
});
|
||||
|
||||
let thread_store = cx.new(|cx| ThreadStore::new(cx));
|
||||
let history = cx.new(|cx| crate::ThreadHistory::new(None, cx));
|
||||
|
||||
// Add editor to workspace
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
@@ -2188,7 +2182,7 @@ pub mod test {
|
||||
project.downgrade(),
|
||||
thread_store,
|
||||
None,
|
||||
history.downgrade(),
|
||||
None,
|
||||
Some(prompt),
|
||||
window,
|
||||
cx,
|
||||
@@ -2198,7 +2192,7 @@ pub mod test {
|
||||
inline_assistant.start_assist(assist_id, window, cx);
|
||||
});
|
||||
|
||||
(editor, buffer, history)
|
||||
(editor, buffer)
|
||||
});
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
@@ -64,7 +64,7 @@ pub struct PromptEditor<T> {
|
||||
pub editor: Entity<Editor>,
|
||||
mode: PromptEditorMode,
|
||||
mention_set: Entity<MentionSet>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
model_selector: Entity<AgentModelSelector>,
|
||||
@@ -1227,7 +1227,7 @@ impl PromptEditor<BufferCodegen> {
|
||||
fs: Arc<dyn Fs>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
project: WeakEntity<Project>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
window: &mut Window,
|
||||
@@ -1386,7 +1386,7 @@ impl PromptEditor<TerminalCodegen> {
|
||||
fs: Arc<dyn Fs>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
project: WeakEntity<Project>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
window: &mut Window,
|
||||
|
||||
@@ -110,7 +110,7 @@ impl MessageEditor {
|
||||
workspace: WeakEntity<Workspace>,
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
|
||||
available_commands: Rc<RefCell<Vec<acp::AvailableCommand>>>,
|
||||
@@ -1789,7 +1789,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = None;
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -1797,7 +1796,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -1902,7 +1901,6 @@ mod tests {
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
let workspace_handle = workspace.downgrade();
|
||||
let message_editor = workspace.update_in(cx, |_, window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -1910,7 +1908,7 @@ mod tests {
|
||||
workspace_handle.clone(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
prompt_capabilities.clone(),
|
||||
available_commands.clone(),
|
||||
@@ -2057,7 +2055,6 @@ mod tests {
|
||||
let mut cx = VisualTestContext::from_window(window.into(), cx);
|
||||
|
||||
let thread_store = None;
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
let prompt_capabilities = Rc::new(RefCell::new(acp::PromptCapabilities::default()));
|
||||
let available_commands = Rc::new(RefCell::new(vec![
|
||||
acp::AvailableCommand::new("quick-math", "2 + 2 = 4 - 1 = 3"),
|
||||
@@ -2075,7 +2072,7 @@ mod tests {
|
||||
workspace_handle,
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
prompt_capabilities.clone(),
|
||||
available_commands.clone(),
|
||||
@@ -2291,7 +2288,6 @@ mod tests {
|
||||
}
|
||||
|
||||
let thread_store = cx.new(|cx| ThreadStore::new(cx));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
let prompt_capabilities = Rc::new(RefCell::new(acp::PromptCapabilities::default()));
|
||||
|
||||
let (message_editor, editor) = workspace.update_in(&mut cx, |workspace, window, cx| {
|
||||
@@ -2301,7 +2297,7 @@ mod tests {
|
||||
workspace_handle,
|
||||
project.downgrade(),
|
||||
Some(thread_store),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
prompt_capabilities.clone(),
|
||||
Default::default(),
|
||||
@@ -2786,7 +2782,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = Some(cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -2794,7 +2789,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -2886,7 +2881,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = Some(cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let session_id = acp::SessionId::new("thread-123");
|
||||
let title = Some("Previous Conversation".into());
|
||||
@@ -2897,7 +2891,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -2961,7 +2955,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = None;
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -2969,7 +2962,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3017,7 +3010,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = None;
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -3025,7 +3017,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3071,7 +3063,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = Some(cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -3079,7 +3070,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3126,7 +3117,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = Some(cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -3134,7 +3124,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3190,7 +3180,6 @@ mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = Some(cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let (message_editor, editor) = workspace.update_in(cx, |workspace, window, cx| {
|
||||
let workspace_handle = cx.weak_entity();
|
||||
@@ -3199,7 +3188,7 @@ mod tests {
|
||||
workspace_handle,
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3349,7 +3338,6 @@ mod tests {
|
||||
});
|
||||
|
||||
let thread_store = Some(cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
// Create a new `MessageEditor`. The `EditorMode::full()` has to be used
|
||||
// to ensure we have a fixed viewport, so we can eventually actually
|
||||
@@ -3361,7 +3349,7 @@ mod tests {
|
||||
workspace_handle,
|
||||
project.downgrade(),
|
||||
thread_store.clone(),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3469,7 +3457,6 @@ mod tests {
|
||||
let mut cx = VisualTestContext::from_window(window.into(), cx);
|
||||
|
||||
let thread_store = cx.new(|cx| ThreadStore::new(cx));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let (message_editor, editor) = workspace.update_in(&mut cx, |workspace, window, cx| {
|
||||
let workspace_handle = cx.weak_entity();
|
||||
@@ -3478,7 +3465,7 @@ mod tests {
|
||||
workspace_handle,
|
||||
project.downgrade(),
|
||||
Some(thread_store),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3551,7 +3538,6 @@ mod tests {
|
||||
let mut cx = VisualTestContext::from_window(window.into(), cx);
|
||||
|
||||
let thread_store = cx.new(|cx| ThreadStore::new(cx));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let (message_editor, editor) = workspace.update_in(&mut cx, |workspace, window, cx| {
|
||||
let workspace_handle = cx.weak_entity();
|
||||
@@ -3560,7 +3546,7 @@ mod tests {
|
||||
workspace_handle,
|
||||
project.downgrade(),
|
||||
Some(thread_store),
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3635,7 +3621,6 @@ mod tests {
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -3643,7 +3628,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
None,
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
@@ -3787,7 +3772,6 @@ mod tests {
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| crate::ThreadHistory::new(None, cx)));
|
||||
|
||||
let message_editor = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
@@ -3795,7 +3779,7 @@ mod tests {
|
||||
workspace.downgrade(),
|
||||
project.downgrade(),
|
||||
None,
|
||||
history.downgrade(),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
|
||||
@@ -64,7 +64,7 @@ impl TerminalInlineAssistant {
|
||||
project: WeakEntity<Project>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: WeakEntity<ThreadHistory>,
|
||||
history: Option<WeakEntity<ThreadHistory>>,
|
||||
initial_prompt: Option<String>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
|
||||
@@ -5,59 +5,48 @@ use std::rc::Rc;
|
||||
use ui::prelude::*;
|
||||
|
||||
pub struct ThreadHistory {
|
||||
session_list: Option<Rc<dyn AgentSessionList>>,
|
||||
session_list: Rc<dyn AgentSessionList>,
|
||||
sessions: Vec<AgentSessionInfo>,
|
||||
_refresh_task: Task<()>,
|
||||
_watch_task: Option<Task<()>>,
|
||||
}
|
||||
|
||||
impl ThreadHistory {
|
||||
pub fn new(session_list: Option<Rc<dyn AgentSessionList>>, cx: &mut Context<Self>) -> Self {
|
||||
pub fn new(session_list: Rc<dyn AgentSessionList>, cx: &mut Context<Self>) -> Self {
|
||||
let mut this = Self {
|
||||
session_list: None,
|
||||
session_list,
|
||||
sessions: Vec::new(),
|
||||
_refresh_task: Task::ready(()),
|
||||
_watch_task: None,
|
||||
};
|
||||
this.set_session_list_impl(session_list, cx);
|
||||
|
||||
this.start_watching(cx);
|
||||
this
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn set_session_list(
|
||||
&mut self,
|
||||
session_list: Option<Rc<dyn AgentSessionList>>,
|
||||
session_list: Rc<dyn AgentSessionList>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.set_session_list_impl(session_list, cx);
|
||||
}
|
||||
|
||||
fn set_session_list_impl(
|
||||
&mut self,
|
||||
session_list: Option<Rc<dyn AgentSessionList>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let (Some(current), Some(next)) = (&self.session_list, &session_list)
|
||||
&& Rc::ptr_eq(current, next)
|
||||
{
|
||||
if Rc::ptr_eq(&self.session_list, &session_list) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.session_list = session_list;
|
||||
self.sessions.clear();
|
||||
self._refresh_task = Task::ready(());
|
||||
self.start_watching(cx);
|
||||
}
|
||||
|
||||
let Some(session_list) = self.session_list.as_ref() else {
|
||||
self._watch_task = None;
|
||||
cx.notify();
|
||||
return;
|
||||
};
|
||||
let Some(rx) = session_list.watch(cx) else {
|
||||
fn start_watching(&mut self, cx: &mut Context<Self>) {
|
||||
let Some(rx) = self.session_list.watch(cx) else {
|
||||
self._watch_task = None;
|
||||
self.refresh_sessions(false, cx);
|
||||
return;
|
||||
};
|
||||
session_list.notify_refresh();
|
||||
self.session_list.notify_refresh();
|
||||
|
||||
self._watch_task = Some(cx.spawn(async move |this, cx| {
|
||||
while let Ok(first_update) = rx.recv().await {
|
||||
@@ -132,10 +121,7 @@ impl ThreadHistory {
|
||||
}
|
||||
|
||||
fn refresh_sessions(&mut self, load_all_pages: bool, cx: &mut Context<Self>) {
|
||||
let Some(session_list) = self.session_list.clone() else {
|
||||
cx.notify();
|
||||
return;
|
||||
};
|
||||
let session_list = self.session_list.clone();
|
||||
|
||||
self._refresh_task = cx.spawn(async move |this, cx| {
|
||||
let mut cursor: Option<String> = None;
|
||||
@@ -196,14 +182,8 @@ impl ThreadHistory {
|
||||
self.sessions.is_empty()
|
||||
}
|
||||
|
||||
pub fn has_session_list(&self) -> bool {
|
||||
self.session_list.is_some()
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self, _cx: &mut Context<Self>) {
|
||||
if let Some(session_list) = &self.session_list {
|
||||
session_list.notify_refresh();
|
||||
}
|
||||
self.session_list.notify_refresh();
|
||||
}
|
||||
|
||||
pub fn session_for_id(&self, session_id: &acp::SessionId) -> Option<AgentSessionInfo> {
|
||||
@@ -222,10 +202,7 @@ impl ThreadHistory {
|
||||
}
|
||||
|
||||
pub fn supports_delete(&self) -> bool {
|
||||
self.session_list
|
||||
.as_ref()
|
||||
.map(|sl| sl.supports_delete())
|
||||
.unwrap_or(false)
|
||||
self.session_list.supports_delete()
|
||||
}
|
||||
|
||||
pub(crate) fn delete_session(
|
||||
@@ -233,19 +210,11 @@ impl ThreadHistory {
|
||||
session_id: &acp::SessionId,
|
||||
cx: &mut App,
|
||||
) -> Task<anyhow::Result<()>> {
|
||||
if let Some(session_list) = self.session_list.as_ref() {
|
||||
session_list.delete_session(session_id, cx)
|
||||
} else {
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
self.session_list.delete_session(session_id, cx)
|
||||
}
|
||||
|
||||
pub(crate) fn delete_sessions(&self, cx: &mut App) -> Task<anyhow::Result<()>> {
|
||||
if let Some(session_list) = self.session_list.as_ref() {
|
||||
session_list.delete_sessions(cx)
|
||||
} else {
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
self.session_list.delete_sessions(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +394,7 @@ mod tests {
|
||||
vec![test_session("session-2", "Second")],
|
||||
));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
history.update(cx, |history, _cx| {
|
||||
@@ -447,7 +416,7 @@ mod tests {
|
||||
vec![test_session("session-2", "Second")],
|
||||
));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
session_list.clear_requested_cursors();
|
||||
|
||||
@@ -482,7 +451,7 @@ mod tests {
|
||||
vec![test_session("session-2", "Second")],
|
||||
));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
history.update(cx, |history, cx| history.refresh_full_history(cx));
|
||||
@@ -513,7 +482,7 @@ mod tests {
|
||||
vec![test_session("session-2", "Second")],
|
||||
));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
history.update(cx, |history, cx| history.refresh_full_history(cx));
|
||||
@@ -542,7 +511,7 @@ mod tests {
|
||||
vec![test_session("session-2", "Second")],
|
||||
));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
history.update(cx, |history, cx| history.refresh_full_history(cx));
|
||||
@@ -585,7 +554,7 @@ mod tests {
|
||||
.with_async_responses(),
|
||||
);
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
session_list.clear_requested_cursors();
|
||||
|
||||
@@ -616,7 +585,7 @@ mod tests {
|
||||
}];
|
||||
let session_list = Rc::new(TestSessionList::new(sessions));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
session_list.send_update(SessionListUpdate::SessionInfo {
|
||||
@@ -649,7 +618,7 @@ mod tests {
|
||||
}];
|
||||
let session_list = Rc::new(TestSessionList::new(sessions));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
session_list.send_update(SessionListUpdate::SessionInfo {
|
||||
@@ -679,7 +648,7 @@ mod tests {
|
||||
}];
|
||||
let session_list = Rc::new(TestSessionList::new(sessions));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
session_list.send_update(SessionListUpdate::SessionInfo {
|
||||
@@ -712,7 +681,7 @@ mod tests {
|
||||
}];
|
||||
let session_list = Rc::new(TestSessionList::new(sessions));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
session_list.send_update(SessionListUpdate::SessionInfo {
|
||||
@@ -749,7 +718,7 @@ mod tests {
|
||||
}];
|
||||
let session_list = Rc::new(TestSessionList::new(sessions));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
session_list.send_update(SessionListUpdate::SessionInfo {
|
||||
@@ -783,7 +752,7 @@ mod tests {
|
||||
}];
|
||||
let session_list = Rc::new(TestSessionList::new(sessions));
|
||||
|
||||
let history = cx.new(|cx| ThreadHistory::new(Some(session_list.clone()), cx));
|
||||
let history = cx.new(|cx| ThreadHistory::new(session_list.clone(), cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
session_list.send_update(SessionListUpdate::SessionInfo {
|
||||
|
||||
@@ -91,6 +91,22 @@ fn fuzzy_match_positions(query: &str, text: &str) -> Option<Vec<usize>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn archive_empty_state_message(
|
||||
has_history: bool,
|
||||
is_empty: bool,
|
||||
has_query: bool,
|
||||
) -> Option<&'static str> {
|
||||
if !is_empty {
|
||||
None
|
||||
} else if !has_history {
|
||||
Some("This agent does not support viewing archived threads.")
|
||||
} else if has_query {
|
||||
Some("No threads match your search.")
|
||||
} else {
|
||||
Some("No archived threads yet.")
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ThreadsArchiveViewEvent {
|
||||
Close,
|
||||
OpenThread {
|
||||
@@ -171,6 +187,7 @@ impl ThreadsArchiveView {
|
||||
fn set_selected_agent(&mut self, agent: Agent, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.selected_agent = agent.clone();
|
||||
self.is_loading = true;
|
||||
self.reset_history_subscription();
|
||||
self.history = None;
|
||||
self.items.clear();
|
||||
self.selection = None;
|
||||
@@ -193,25 +210,33 @@ impl ThreadsArchiveView {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn set_history(&mut self, history: Entity<ThreadHistory>, cx: &mut Context<Self>) {
|
||||
self._history_subscription = cx.observe(&history, |this, _, cx| {
|
||||
this.update_items(cx);
|
||||
});
|
||||
history.update(cx, |history, cx| {
|
||||
history.refresh_full_history(cx);
|
||||
});
|
||||
self.history = Some(history);
|
||||
fn reset_history_subscription(&mut self) {
|
||||
self._history_subscription = Subscription::new(|| {});
|
||||
}
|
||||
|
||||
fn set_history(&mut self, history: Option<Entity<ThreadHistory>>, cx: &mut Context<Self>) {
|
||||
self.reset_history_subscription();
|
||||
|
||||
if let Some(history) = &history {
|
||||
self._history_subscription = cx.observe(history, |this, _, cx| {
|
||||
this.update_items(cx);
|
||||
});
|
||||
history.update(cx, |history, cx| {
|
||||
history.refresh_full_history(cx);
|
||||
});
|
||||
}
|
||||
self.history = history;
|
||||
self.is_loading = false;
|
||||
self.update_items(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn update_items(&mut self, cx: &mut Context<Self>) {
|
||||
let Some(history) = self.history.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let sessions = history.read(cx).sessions().to_vec();
|
||||
let sessions = self
|
||||
.history
|
||||
.as_ref()
|
||||
.map(|h| h.read(cx).sessions().to_vec())
|
||||
.unwrap_or_default();
|
||||
let query = self.filter_editor.read(cx).text(cx).to_lowercase();
|
||||
let today = Local::now().naive_local().date();
|
||||
|
||||
@@ -696,6 +721,12 @@ impl Focusable for ThreadsArchiveView {
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadsArchiveView {
|
||||
fn empty_state_message(&self, is_empty: bool, has_query: bool) -> Option<&'static str> {
|
||||
archive_empty_state_message(self.history.is_some(), is_empty, has_query)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ThreadsArchiveView {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let is_empty = self.items.is_empty();
|
||||
@@ -713,24 +744,13 @@ impl Render for ThreadsArchiveView {
|
||||
.with_rotate_animation(2),
|
||||
)
|
||||
.into_any_element()
|
||||
} else if is_empty && has_query {
|
||||
} else if let Some(message) = self.empty_state_message(is_empty, has_query) {
|
||||
v_flex()
|
||||
.flex_1()
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.child(
|
||||
Label::new("No threads match your search.")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.into_any_element()
|
||||
} else if is_empty {
|
||||
v_flex()
|
||||
.flex_1()
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.child(
|
||||
Label::new("No archived threads yet.")
|
||||
Label::new(message)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
@@ -768,3 +788,38 @@ impl Render for ThreadsArchiveView {
|
||||
.child(content)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::archive_empty_state_message;
|
||||
|
||||
#[test]
|
||||
fn empty_state_message_returns_none_when_archive_has_items() {
|
||||
assert_eq!(archive_empty_state_message(false, false, false), None);
|
||||
assert_eq!(archive_empty_state_message(true, false, true), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_state_message_distinguishes_unsupported_history() {
|
||||
assert_eq!(
|
||||
archive_empty_state_message(false, true, false),
|
||||
Some("This agent does not support viewing archived threads.")
|
||||
);
|
||||
assert_eq!(
|
||||
archive_empty_state_message(false, true, true),
|
||||
Some("This agent does not support viewing archived threads.")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_state_message_distinguishes_empty_history_and_search_results() {
|
||||
assert_eq!(
|
||||
archive_empty_state_message(true, true, false),
|
||||
Some("No archived threads yet.")
|
||||
);
|
||||
assert_eq!(
|
||||
archive_empty_state_message(true, true, true),
|
||||
Some("No threads match your search.")
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user