Move UI code from assistant_context_editor -> agent_ui (#33289)

This breaks a transitive dependency of `agent` on UI crates. I've also
found and eliminated some dead code in assistant_context_editor.

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2025-06-23 22:22:01 -07:00 committed by GitHub
parent 786e724684
commit 4cd4d28531
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 144 additions and 455 deletions

23
Cargo.lock generated
View file

@ -55,7 +55,7 @@ version = "0.1.0"
dependencies = [
"agent_settings",
"anyhow",
"assistant_context_editor",
"assistant_context",
"assistant_tool",
"assistant_tools",
"chrono",
@ -138,7 +138,7 @@ dependencies = [
"agent",
"agent_settings",
"anyhow",
"assistant_context_editor",
"assistant_context",
"assistant_slash_command",
"assistant_slash_commands",
"assistant_tool",
@ -169,6 +169,7 @@ dependencies = [
"jsonschema",
"language",
"language_model",
"languages",
"log",
"lsp",
"markdown",
@ -203,7 +204,9 @@ dependencies = [
"theme",
"time",
"time_format",
"tree-sitter-md",
"ui",
"unindent",
"urlencoding",
"util",
"uuid",
@ -557,7 +560,7 @@ dependencies = [
]
[[package]]
name = "assistant_context_editor"
name = "assistant_context"
version = "0.1.0"
dependencies = [
"agent_settings",
@ -569,31 +572,23 @@ dependencies = [
"clock",
"collections",
"context_server",
"editor",
"feature_flags",
"fs",
"futures 0.3.31",
"fuzzy",
"gpui",
"indexed_docs",
"indoc",
"language",
"language_model",
"languages",
"log",
"multi_buffer",
"open_ai",
"ordered-float 2.10.1",
"parking_lot",
"paths",
"picker",
"pretty_assertions",
"project",
"prompt_store",
"proto",
"rand 0.8.5",
"regex",
"rope",
"rpc",
"serde",
"serde_json",
@ -602,15 +597,12 @@ dependencies = [
"smol",
"telemetry_events",
"text",
"theme",
"tree-sitter-md",
"ui",
"unindent",
"util",
"uuid",
"workspace",
"workspace-hack",
"zed_actions",
"zed_llm_client",
]
@ -3031,7 +3023,7 @@ version = "0.44.0"
dependencies = [
"agent_settings",
"anyhow",
"assistant_context_editor",
"assistant_context",
"assistant_slash_command",
"async-stripe",
"async-trait",
@ -19924,7 +19916,6 @@ dependencies = [
"ashpd",
"askpass",
"assets",
"assistant_context_editor",
"assistant_tool",
"assistant_tools",
"audio",

View file

@ -8,7 +8,7 @@ members = [
"crates/anthropic",
"crates/askpass",
"crates/assets",
"crates/assistant_context_editor",
"crates/assistant_context",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
"crates/assistant_tool",
@ -221,7 +221,7 @@ ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
assets = { path = "crates/assets" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_context = { path = "crates/assistant_context" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
assistant_tool = { path = "crates/assistant_tool" }

View file

@ -21,7 +21,7 @@ test-support = [
[dependencies]
agent_settings.workspace = true
anyhow.workspace = true
assistant_context_editor.workspace = true
assistant_context.workspace = true
assistant_tool.workspace = true
chrono.workspace = true
client.workspace = true

View file

@ -1,5 +1,5 @@
use crate::thread::Thread;
use assistant_context_editor::AssistantContext;
use assistant_context::AssistantContext;
use assistant_tool::outline;
use collections::HashSet;
use futures::future;

View file

@ -8,7 +8,7 @@ use crate::{
thread_store::ThreadStore,
};
use anyhow::{Context as _, Result, anyhow};
use assistant_context_editor::AssistantContext;
use assistant_context::AssistantContext;
use collections::{HashSet, IndexSet};
use futures::{self, FutureExt};
use gpui::{App, Context, Entity, EventEmitter, Image, SharedString, Task, WeakEntity};

View file

@ -3,7 +3,7 @@ use crate::{
thread_store::{SerializedThreadMetadata, ThreadStore},
};
use anyhow::{Context as _, Result};
use assistant_context_editor::SavedContextMetadata;
use assistant_context::SavedContextMetadata;
use chrono::{DateTime, Utc};
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
use itertools::Itertools;
@ -62,7 +62,7 @@ enum SerializedRecentOpen {
pub struct HistoryStore {
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
context_store: Entity<assistant_context::ContextStore>,
recently_opened_entries: VecDeque<HistoryEntryId>,
_subscriptions: Vec<gpui::Subscription>,
_save_recently_opened_entries_task: Task<()>,
@ -71,7 +71,7 @@ pub struct HistoryStore {
impl HistoryStore {
pub fn new(
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
context_store: Entity<assistant_context::ContextStore>,
initial_recent_entries: impl IntoIterator<Item = HistoryEntryId>,
cx: &mut Context<Self>,
) -> Self {

View file

@ -96,7 +96,7 @@ impl SharedProjectContext {
}
}
pub type TextThreadStore = assistant_context_editor::ContextStore;
pub type TextThreadStore = assistant_context::ContextStore;
pub struct ThreadStore {
project: Entity<Project>,

View file

@ -22,7 +22,7 @@ test-support = [
agent.workspace = true
agent_settings.workspace = true
anyhow.workspace = true
assistant_context_editor.workspace = true
assistant_context.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
@ -101,7 +101,10 @@ editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true

View file

@ -1,13 +1,14 @@
use crate::{
ModelUsageContext,
language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
},
};
use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
use crate::ModelUsageContext;
use assistant_context_editor::language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
};
use language_model::{ConfiguredModel, LanguageModelRegistry};
use picker::popover_menu::PickerPopoverMenu;
use settings::update_settings_file;
use std::sync::Arc;
use ui::{PopoverMenuHandle, Tooltip, prelude::*};

View file

@ -7,17 +7,35 @@ use std::time::Duration;
use db::kvp::{Dismissable, KEY_VALUE_STORE};
use serde::{Deserialize, Serialize};
use crate::language_model_selector::ToggleModelSelector;
use crate::{
AddContextServer, AgentDiffPane, ContinueThread, ContinueWithBurnMode,
DeleteRecentlyOpenThread, ExpandMessageEditor, Follow, InlineAssistant, NewTextThread,
NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell,
ResetTrialUpsell, ToggleBurnMode, ToggleContextPicker, ToggleNavigationMenu, ToggleOptionsMenu,
active_thread::{self, ActiveThread, ActiveThreadEvent},
agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
agent_diff::AgentDiff,
message_editor::{MessageEditor, MessageEditorEvent},
slash_command::SlashCommandCompletionProvider,
text_thread_editor::{
AgentPanelDelegate, TextThreadEditor, humanize_token_count, make_lsp_adapter_delegate,
render_remaining_tokens,
},
thread_history::{HistoryEntryElement, ThreadHistory},
ui::AgentOnboardingModal,
};
use agent::{
Thread, ThreadError, ThreadEvent, ThreadId, ThreadSummary, TokenUsageRatio,
context_store::ContextStore,
history_store::{HistoryEntryId, HistoryStore},
thread_store::{TextThreadStore, ThreadStore},
};
use agent_settings::{AgentDockPosition, AgentSettings, CompletionMode, DefaultView};
use anyhow::{Result, anyhow};
use assistant_context_editor::{
AgentPanelDelegate, AssistantContext, ContextEditor, ContextEvent, ContextSummary,
SlashCommandCompletionProvider, humanize_token_count, make_lsp_adapter_delegate,
render_remaining_tokens,
};
use assistant_context::{AssistantContext, ContextEvent, ContextSummary};
use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;
use assistant_context_editor::language_model_selector::ToggleModelSelector;
use client::{UserStore, zed_urls};
use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer};
use fs::Fs;
@ -56,25 +74,6 @@ use zed_actions::{
};
use zed_llm_client::{CompletionIntent, UsageLimit};
use crate::{
AddContextServer, AgentDiffPane, ContinueThread, ContinueWithBurnMode,
DeleteRecentlyOpenThread, ExpandMessageEditor, Follow, InlineAssistant, NewTextThread,
NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell,
ResetTrialUpsell, ToggleBurnMode, ToggleContextPicker, ToggleNavigationMenu, ToggleOptionsMenu,
active_thread::{self, ActiveThread, ActiveThreadEvent},
agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
agent_diff::AgentDiff,
message_editor::{MessageEditor, MessageEditorEvent},
thread_history::{HistoryEntryElement, ThreadHistory},
ui::AgentOnboardingModal,
};
use agent::{
Thread, ThreadError, ThreadEvent, ThreadId, ThreadSummary, TokenUsageRatio,
context_store::ContextStore,
history_store::{HistoryEntryId, HistoryStore},
thread_store::{TextThreadStore, ThreadStore},
};
const AGENT_PANEL_KEY: &str = "agent_panel";
#[derive(Serialize, Deserialize)]
@ -179,7 +178,7 @@ enum ActiveView {
_subscriptions: Vec<gpui::Subscription>,
},
TextThread {
context_editor: Entity<ContextEditor>,
context_editor: Entity<TextThreadEditor>,
title_editor: Entity<Editor>,
buffer_search_bar: Entity<BufferSearchBar>,
_subscriptions: Vec<gpui::Subscription>,
@ -260,7 +259,7 @@ impl ActiveView {
}
pub fn prompt_editor(
context_editor: Entity<ContextEditor>,
context_editor: Entity<TextThreadEditor>,
history_store: Entity<HistoryStore>,
language_registry: Arc<LanguageRegistry>,
window: &mut Window,
@ -434,7 +433,7 @@ impl AgentPanel {
let context_store = workspace
.update(cx, |workspace, cx| {
let project = workspace.project().clone();
assistant_context_editor::ContextStore::new(
assistant_context::ContextStore::new(
project,
prompt_builder.clone(),
slash_commands,
@ -546,7 +545,7 @@ impl AgentPanel {
context_store.update(cx, |context_store, cx| context_store.create(cx));
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project.clone(), cx).unwrap();
let context_editor = cx.new(|cx| {
let mut editor = ContextEditor::for_context(
let mut editor = TextThreadEditor::for_context(
context,
fs.clone(),
workspace.clone(),
@ -841,7 +840,7 @@ impl AgentPanel {
.flatten();
let context_editor = cx.new(|cx| {
let mut editor = ContextEditor::for_context(
let mut editor = TextThreadEditor::for_context(
context,
self.fs.clone(),
self.workspace.clone(),
@ -933,7 +932,7 @@ impl AgentPanel {
.log_err()
.flatten();
let editor = cx.new(|cx| {
ContextEditor::for_context(
TextThreadEditor::for_context(
context,
self.fs.clone(),
self.workspace.clone(),
@ -1321,7 +1320,7 @@ impl AgentPanel {
});
}
pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> {
pub(crate) fn active_context_editor(&self) -> Option<Entity<TextThreadEditor>> {
match &self.active_view {
ActiveView::TextThread { context_editor, .. } => Some(context_editor.clone()),
_ => None,
@ -2899,7 +2898,7 @@ impl AgentPanel {
fn render_prompt_editor(
&self,
context_editor: &Entity<ContextEditor>,
context_editor: &Entity<TextThreadEditor>,
buffer_search_bar: &Entity<BufferSearchBar>,
window: &mut Window,
cx: &mut Context<Self>,
@ -3026,7 +3025,7 @@ impl AgentPanel {
}
ActiveView::TextThread { context_editor, .. } => {
context_editor.update(cx, |context_editor, cx| {
ContextEditor::insert_dragged_files(
TextThreadEditor::insert_dragged_files(
context_editor,
paths,
added_worktrees,
@ -3205,7 +3204,7 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate {
workspace: &mut Workspace,
_window: &mut Window,
cx: &mut Context<Workspace>,
) -> Option<Entity<ContextEditor>> {
) -> Option<Entity<TextThreadEditor>> {
let panel = workspace.panel::<AgentPanel>(cx)?;
panel.read(cx).active_context_editor()
}
@ -3229,10 +3228,10 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate {
fn open_remote_context(
&self,
_workspace: &mut Workspace,
_context_id: assistant_context_editor::ContextId,
_context_id: assistant_context::ContextId,
_window: &mut Window,
_cx: &mut Context<Workspace>,
) -> Task<Result<Entity<ContextEditor>>> {
) -> Task<Result<Entity<TextThreadEditor>>> {
Task::ready(Err(anyhow!("opening remote context not implemented")))
}

View file

@ -10,11 +10,16 @@ mod context_strip;
mod debug;
mod inline_assistant;
mod inline_prompt_editor;
mod language_model_selector;
mod max_mode_tooltip;
mod message_editor;
mod profile_selector;
mod slash_command;
mod slash_command_picker;
mod slash_command_settings;
mod terminal_codegen;
mod terminal_inline_assistant;
mod text_thread_editor;
mod thread_history;
mod tool_compatibility;
mod ui;
@ -43,6 +48,7 @@ pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
pub use crate::inline_assistant::InlineAssistant;
use crate::slash_command_settings::SlashCommandSettings;
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
pub use text_thread_editor::AgentPanelDelegate;
pub use ui::preview::{all_agent_previews, get_agent_preview};
actions!(
@ -140,7 +146,7 @@ pub fn init(
AgentSettings::register(cx);
SlashCommandSettings::register(cx);
assistant_context_editor::init(client.clone(), cx);
assistant_context::init(client.clone(), cx);
rules_library::init(cx);
if !is_eval {
// Initializing the language model from the user settings messes with the eval, so we only initialize them when

View file

@ -73,7 +73,7 @@ fn search(
recent_entries: Vec<RecentEntry>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_context_store: Option<WeakEntity<assistant_context_editor::ContextStore>>,
text_thread_context_store: Option<WeakEntity<assistant_context::ContextStore>>,
workspace: Entity<Workspace>,
cx: &mut App,
) -> Task<Vec<Match>> {

View file

@ -2,6 +2,7 @@ use crate::agent_model_selector::AgentModelSelector;
use crate::buffer_codegen::BufferCodegen;
use crate::context_picker::{ContextPicker, ContextPickerCompletionProvider};
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::language_model_selector::ToggleModelSelector;
use crate::message_editor::{ContextCreasesAddon, extract_message_creases, insert_message_creases};
use crate::terminal_codegen::TerminalCodegen;
use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist, ModelUsageContext};
@ -10,7 +11,6 @@ use agent::{
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
use assistant_context_editor::language_model_selector::ToggleModelSelector;
use client::ErrorExt;
use collections::VecDeque;
use db::kvp::Dismissable;

View file

@ -3,6 +3,7 @@ use std::rc::Rc;
use std::sync::Arc;
use crate::agent_model_selector::AgentModelSelector;
use crate::language_model_selector::ToggleModelSelector;
use crate::tool_compatibility::{IncompatibleToolsState, IncompatibleToolsTooltip};
use crate::ui::{
MaxModeTooltip,
@ -13,7 +14,6 @@ use agent::{
context_store::ContextStoreEvent,
};
use agent_settings::{AgentSettings, CompletionMode};
use assistant_context_editor::language_model_selector::ToggleModelSelector;
use buffer_diff::BufferDiff;
use client::UserStore;
use collections::{HashMap, HashSet};

View file

@ -1,4 +1,4 @@
use crate::context_editor::ContextEditor;
use crate::text_thread_editor::TextThreadEditor;
use anyhow::Result;
pub use assistant_slash_command::SlashCommand;
use assistant_slash_command::{AfterCompletion, SlashCommandLine, SlashCommandWorkingSet};
@ -21,14 +21,14 @@ use workspace::Workspace;
pub struct SlashCommandCompletionProvider {
cancel_flag: Mutex<Arc<AtomicBool>>,
slash_commands: Arc<SlashCommandWorkingSet>,
editor: Option<WeakEntity<ContextEditor>>,
editor: Option<WeakEntity<TextThreadEditor>>,
workspace: Option<WeakEntity<Workspace>>,
}
impl SlashCommandCompletionProvider {
pub fn new(
slash_commands: Arc<SlashCommandWorkingSet>,
editor: Option<WeakEntity<ContextEditor>>,
editor: Option<WeakEntity<TextThreadEditor>>,
workspace: Option<WeakEntity<Workspace>>,
) -> Self {
Self {

View file

@ -1,12 +1,10 @@
use std::sync::Arc;
use crate::text_thread_editor::TextThreadEditor;
use assistant_slash_command::SlashCommandWorkingSet;
use gpui::{AnyElement, AnyView, DismissEvent, SharedString, Task, WeakEntity};
use picker::{Picker, PickerDelegate, PickerEditorPosition};
use std::sync::Arc;
use ui::{ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip, prelude::*};
use crate::context_editor::ContextEditor;
#[derive(IntoElement)]
pub(super) struct SlashCommandSelector<T, TT>
where
@ -14,7 +12,7 @@ where
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
{
working_set: Arc<SlashCommandWorkingSet>,
active_context_editor: WeakEntity<ContextEditor>,
active_context_editor: WeakEntity<TextThreadEditor>,
trigger: T,
tooltip: TT,
}
@ -49,7 +47,7 @@ impl AsRef<str> for SlashCommandEntry {
pub(crate) struct SlashCommandDelegate {
all_commands: Vec<SlashCommandEntry>,
filtered_commands: Vec<SlashCommandEntry>,
active_context_editor: WeakEntity<ContextEditor>,
active_context_editor: WeakEntity<TextThreadEditor>,
selected_index: usize,
}
@ -60,7 +58,7 @@ where
{
pub(crate) fn new(
working_set: Arc<SlashCommandWorkingSet>,
active_context_editor: WeakEntity<ContextEditor>,
active_context_editor: WeakEntity<TextThreadEditor>,
trigger: T,
tooltip: TT,
) -> Self {

View file

@ -76,14 +76,11 @@ use workspace::{
searchable::{SearchEvent, SearchableItem},
};
use crate::{
use crate::{slash_command::SlashCommandCompletionProvider, slash_command_picker};
use assistant_context::{
AssistantContext, CacheStatus, Content, ContextEvent, ContextId, InvokedSlashCommandId,
InvokedSlashCommandStatus, Message, MessageId, MessageMetadata, MessageStatus,
ParsedSlashCommand, PendingSlashCommandStatus,
};
use crate::{
ThoughtProcessOutputSection, slash_command::SlashCommandCompletionProvider,
slash_command_picker,
ParsedSlashCommand, PendingSlashCommandStatus, ThoughtProcessOutputSection,
};
actions!(
@ -131,7 +128,7 @@ pub trait AgentPanelDelegate {
workspace: &mut Workspace,
window: &mut Window,
cx: &mut Context<Workspace>,
) -> Option<Entity<ContextEditor>>;
) -> Option<Entity<TextThreadEditor>>;
fn open_saved_context(
&self,
@ -147,7 +144,7 @@ pub trait AgentPanelDelegate {
context_id: ContextId,
window: &mut Window,
cx: &mut Context<Workspace>,
) -> Task<Result<Entity<ContextEditor>>>;
) -> Task<Result<Entity<TextThreadEditor>>>;
fn quote_selection(
&self,
@ -176,7 +173,7 @@ struct GlobalAssistantPanelDelegate(Arc<dyn AgentPanelDelegate>);
impl Global for GlobalAssistantPanelDelegate {}
pub struct ContextEditor {
pub struct TextThreadEditor {
context: Entity<AssistantContext>,
fs: Arc<dyn Fs>,
slash_commands: Arc<SlashCommandWorkingSet>,
@ -206,10 +203,24 @@ pub struct ContextEditor {
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
}
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
const MAX_TAB_TITLE_LEN: usize = 16;
impl ContextEditor {
impl TextThreadEditor {
pub fn init(cx: &mut App) {
workspace::FollowableViewRegistry::register::<TextThreadEditor>(cx);
cx.observe_new(
|workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
workspace
.register_action(TextThreadEditor::quote_selection)
.register_action(TextThreadEditor::insert_selection)
.register_action(TextThreadEditor::copy_code)
.register_action(TextThreadEditor::handle_insert_dragged_files);
},
)
.detach();
}
pub fn for_context(
context: Entity<AssistantContext>,
fs: Arc<dyn Fs>,
@ -1279,7 +1290,7 @@ impl ContextEditor {
/// Returns either the selected text, or the content of the Markdown code
/// block surrounding the cursor.
fn get_selection_or_code_block(
context_editor_view: &Entity<ContextEditor>,
context_editor_view: &Entity<TextThreadEditor>,
cx: &mut Context<Workspace>,
) -> Option<(String, bool)> {
const CODE_FENCE_DELIMITER: &'static str = "```";
@ -2029,7 +2040,7 @@ impl ContextEditor {
/// Whether or not we should allow messages to be sent.
/// Will return false if the selected provided has a configuration error or
/// if the user has not accepted the terms of service for this provider.
fn sending_disabled(&self, cx: &mut Context<'_, ContextEditor>) -> bool {
fn sending_disabled(&self, cx: &mut Context<'_, TextThreadEditor>) -> bool {
let model_registry = LanguageModelRegistry::read_global(cx);
let Some(configuration_error) =
model_registry.configuration_error(model_registry.default_model(), cx)
@ -2546,10 +2557,10 @@ struct SelectedCreaseMetadata {
crease: CreaseMetadata,
}
impl EventEmitter<EditorEvent> for ContextEditor {}
impl EventEmitter<SearchEvent> for ContextEditor {}
impl EventEmitter<EditorEvent> for TextThreadEditor {}
impl EventEmitter<SearchEvent> for TextThreadEditor {}
impl Render for ContextEditor {
impl Render for TextThreadEditor {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let provider = LanguageModelRegistry::read_global(cx)
.default_model()
@ -2568,15 +2579,15 @@ impl Render for ContextEditor {
v_flex()
.key_context("ContextEditor")
.capture_action(cx.listener(ContextEditor::cancel))
.capture_action(cx.listener(ContextEditor::save))
.capture_action(cx.listener(ContextEditor::copy))
.capture_action(cx.listener(ContextEditor::cut))
.capture_action(cx.listener(ContextEditor::paste))
.capture_action(cx.listener(ContextEditor::cycle_message_role))
.capture_action(cx.listener(ContextEditor::confirm_command))
.on_action(cx.listener(ContextEditor::assist))
.on_action(cx.listener(ContextEditor::split))
.capture_action(cx.listener(TextThreadEditor::cancel))
.capture_action(cx.listener(TextThreadEditor::save))
.capture_action(cx.listener(TextThreadEditor::copy))
.capture_action(cx.listener(TextThreadEditor::cut))
.capture_action(cx.listener(TextThreadEditor::paste))
.capture_action(cx.listener(TextThreadEditor::cycle_message_role))
.capture_action(cx.listener(TextThreadEditor::confirm_command))
.on_action(cx.listener(TextThreadEditor::assist))
.on_action(cx.listener(TextThreadEditor::split))
.on_action(move |_: &ToggleModelSelector, window, cx| {
language_model_selector.toggle(window, cx);
})
@ -2631,13 +2642,13 @@ impl Render for ContextEditor {
}
}
impl Focusable for ContextEditor {
impl Focusable for TextThreadEditor {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.editor.focus_handle(cx)
}
}
impl Item for ContextEditor {
impl Item for TextThreadEditor {
type Event = editor::EditorEvent;
fn tab_content_text(&self, _detail: usize, cx: &App) -> SharedString {
@ -2710,7 +2721,7 @@ impl Item for ContextEditor {
}
}
impl SearchableItem for ContextEditor {
impl SearchableItem for TextThreadEditor {
type Match = <Editor as SearchableItem>::Match;
fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@ -2791,7 +2802,7 @@ impl SearchableItem for ContextEditor {
}
}
impl FollowableItem for ContextEditor {
impl FollowableItem for TextThreadEditor {
fn remote_id(&self) -> Option<workspace::ViewId> {
self.remote_id
}
@ -2914,21 +2925,14 @@ impl FollowableItem for ContextEditor {
}
pub struct ContextEditorToolbarItem {
active_context_editor: Option<WeakEntity<ContextEditor>>,
active_context_editor: Option<WeakEntity<TextThreadEditor>>,
model_summary_editor: Entity<Editor>,
}
impl ContextEditorToolbarItem {
pub fn new(model_summary_editor: Entity<Editor>) -> Self {
Self {
active_context_editor: None,
model_summary_editor,
}
}
}
impl ContextEditorToolbarItem {}
pub fn render_remaining_tokens(
context_editor: &Entity<ContextEditor>,
context_editor: &Entity<TextThreadEditor>,
cx: &App,
) -> Option<impl IntoElement + use<>> {
let context = &context_editor.read(cx).context;
@ -3044,7 +3048,7 @@ impl ToolbarItemView for ContextEditorToolbarItem {
cx: &mut Context<Self>,
) -> ToolbarItemLocation {
self.active_context_editor = active_pane_item
.and_then(|item| item.act_as::<ContextEditor>(cx))
.and_then(|item| item.act_as::<TextThreadEditor>(cx))
.map(|editor| editor.downgrade());
cx.notify();
if self.active_context_editor.is_none() {
@ -3405,7 +3409,7 @@ mod tests {
cx: &mut TestAppContext,
) -> (
Entity<AssistantContext>,
Entity<ContextEditor>,
Entity<TextThreadEditor>,
VisualTestContext,
) {
cx.update(init_test);
@ -3421,7 +3425,7 @@ mod tests {
let context_editor = window
.update(&mut cx, |_, window, cx| {
cx.new(|cx| {
let editor = ContextEditor::for_context(
let editor = TextThreadEditor::for_context(
context.clone(),
fs,
workspace.downgrade(),
@ -3454,7 +3458,7 @@ mod tests {
}
fn assert_copy_paste_context_editor<T: editor::ToOffset>(
context_editor: &Entity<ContextEditor>,
context_editor: &Entity<TextThreadEditor>,
range: Range<T>,
expected_text: &str,
cx: &mut VisualTestContext,

View file

@ -1,5 +1,5 @@
[package]
name = "assistant_context_editor"
name = "assistant_context"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
@ -9,7 +9,7 @@ license = "GPL-3.0-or-later"
workspace = true
[lib]
path = "src/assistant_context_editor.rs"
path = "src/assistant_context.rs"
[dependencies]
agent_settings.workspace = true
@ -21,27 +21,20 @@ client.workspace = true
clock.workspace = true
collections.workspace = true
context_server.workspace = true
editor.workspace = true
feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
indexed_docs.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
multi_buffer.workspace = true
open_ai.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
regex.workspace = true
rope.workspace = true
rpc.workspace = true
serde.workspace = true
serde_json.workspace = true
@ -50,21 +43,17 @@ smallvec.workspace = true
smol.workspace = true
telemetry_events.workspace = true
text.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
[dev-dependencies]
indoc.workspace = true
language_model = { workspace = true, features = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
rand.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true
workspace = { workspace = true, features = ["test-support"] }

View file

@ -1,5 +1,6 @@
#[cfg(test)]
mod context_tests;
mod assistant_context_tests;
mod context_store;
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result, bail};
@ -8,7 +9,7 @@ use assistant_slash_command::{
SlashCommandResult, SlashCommandWorkingSet,
};
use assistant_slash_commands::FileCommandMetadata;
use client::{self, proto, telemetry::Telemetry};
use client::{self, Client, proto, telemetry::Telemetry};
use clock::ReplicaId;
use collections::{HashMap, HashSet};
use fs::{Fs, RenameOptions};
@ -47,6 +48,12 @@ use util::{ResultExt, TryFutureExt, post_inc};
use uuid::Uuid;
use zed_llm_client::CompletionIntent;
pub use crate::context_store::*;
pub fn init(client: Arc<Client>, _: &mut App) {
context_store::init(&client.into());
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ContextId(String);

View file

@ -1,36 +0,0 @@
mod context;
mod context_editor;
mod context_history;
mod context_store;
pub mod language_model_selector;
mod max_mode_tooltip;
mod slash_command;
mod slash_command_picker;
use std::sync::Arc;
use client::Client;
use gpui::{App, Context};
use workspace::Workspace;
pub use crate::context::*;
pub use crate::context_editor::*;
pub use crate::context_history::*;
pub use crate::context_store::*;
pub use crate::slash_command::*;
pub fn init(client: Arc<Client>, cx: &mut App) {
context_store::init(&client.into());
workspace::FollowableViewRegistry::register::<ContextEditor>(cx);
cx.observe_new(
|workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
workspace
.register_action(ContextEditor::quote_selection)
.register_action(ContextEditor::insert_selection)
.register_action(ContextEditor::copy_code)
.register_action(ContextEditor::handle_insert_dragged_files);
},
)
.detach();
}

View file

@ -1,271 +0,0 @@
use std::sync::Arc;
use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity};
use picker::{Picker, PickerDelegate};
use project::Project;
use ui::utils::{DateTimeType, format_distance_from_now};
use ui::{Avatar, ListItem, ListItemSpacing, prelude::*};
use workspace::{Item, Workspace};
use crate::{
AgentPanelDelegate, ContextStore, DEFAULT_TAB_TITLE, RemoteContextMetadata,
SavedContextMetadata,
};
#[derive(Clone)]
pub enum ContextMetadata {
Remote(RemoteContextMetadata),
Saved(SavedContextMetadata),
}
enum SavedContextPickerEvent {
Confirmed(ContextMetadata),
}
pub struct ContextHistory {
picker: Entity<Picker<SavedContextPickerDelegate>>,
_subscriptions: Vec<Subscription>,
workspace: WeakEntity<Workspace>,
}
impl ContextHistory {
pub fn new(
project: Entity<Project>,
context_store: Entity<ContextStore>,
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let picker = cx.new(|cx| {
Picker::uniform_list(
SavedContextPickerDelegate::new(project, context_store.clone()),
window,
cx,
)
.modal(false)
.max_height(None)
});
let subscriptions = vec![
cx.observe_in(&context_store, window, |this, _, window, cx| {
this.picker
.update(cx, |picker, cx| picker.refresh(window, cx));
}),
cx.subscribe_in(&picker, window, Self::handle_picker_event),
];
Self {
picker,
_subscriptions: subscriptions,
workspace,
}
}
fn handle_picker_event(
&mut self,
_: &Entity<Picker<SavedContextPickerDelegate>>,
event: &SavedContextPickerEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
let SavedContextPickerEvent::Confirmed(context) = event;
let Some(agent_panel_delegate) = <dyn AgentPanelDelegate>::try_global(cx) else {
return;
};
self.workspace
.update(cx, |workspace, cx| match context {
ContextMetadata::Remote(metadata) => {
agent_panel_delegate
.open_remote_context(workspace, metadata.id.clone(), window, cx)
.detach_and_log_err(cx);
}
ContextMetadata::Saved(metadata) => {
agent_panel_delegate
.open_saved_context(workspace, metadata.path.clone(), window, cx)
.detach_and_log_err(cx);
}
})
.ok();
}
}
impl Render for ContextHistory {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div().size_full().child(self.picker.clone())
}
}
impl Focusable for ContextHistory {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl EventEmitter<()> for ContextHistory {}
impl Item for ContextHistory {
type Event = ();
fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
"History".into()
}
}
struct SavedContextPickerDelegate {
store: Entity<ContextStore>,
project: Entity<Project>,
matches: Vec<ContextMetadata>,
selected_index: usize,
}
impl EventEmitter<SavedContextPickerEvent> for Picker<SavedContextPickerDelegate> {}
impl SavedContextPickerDelegate {
fn new(project: Entity<Project>, store: Entity<ContextStore>) -> Self {
Self {
project,
store,
matches: Vec::new(),
selected_index: 0,
}
}
}
impl PickerDelegate for SavedContextPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.matches.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search...".into()
}
fn update_matches(
&mut self,
query: String,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let search = self.store.read(cx).search(query, cx);
cx.spawn(async move |this, cx| {
let matches = search.await;
this.update(cx, |this, cx| {
let host_contexts = this.delegate.store.read(cx).host_contexts();
this.delegate.matches = host_contexts
.iter()
.cloned()
.map(ContextMetadata::Remote)
.chain(matches.into_iter().map(ContextMetadata::Saved))
.collect();
this.delegate.selected_index = 0;
cx.notify();
})
.ok();
})
}
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
if let Some(metadata) = self.matches.get(self.selected_index) {
cx.emit(SavedContextPickerEvent::Confirmed(metadata.clone()));
}
}
fn dismissed(&mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>) {}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let context = self.matches.get(ix)?;
let item = match context {
ContextMetadata::Remote(context) => {
let host_user = self.project.read(cx).host().and_then(|collaborator| {
self.project
.read(cx)
.user_store()
.read(cx)
.get_cached_user(collaborator.user_id)
});
div()
.flex()
.w_full()
.justify_between()
.gap_2()
.child(
h_flex().flex_1().overflow_x_hidden().child(
Label::new(context.summary.clone().unwrap_or(DEFAULT_TAB_TITLE.into()))
.size(LabelSize::Small),
),
)
.child(
h_flex()
.gap_2()
.children(if let Some(host_user) = host_user {
vec![
Avatar::new(host_user.avatar_uri.clone()).into_any_element(),
Label::new(format!("Shared by @{}", host_user.github_login))
.color(Color::Muted)
.size(LabelSize::Small)
.into_any_element(),
]
} else {
vec![
Label::new("Shared by host")
.color(Color::Muted)
.size(LabelSize::Small)
.into_any_element(),
]
}),
)
}
ContextMetadata::Saved(context) => div()
.flex()
.w_full()
.justify_between()
.gap_2()
.child(
h_flex()
.flex_1()
.child(Label::new(context.title.clone()).size(LabelSize::Small))
.overflow_x_hidden(),
)
.child(
Label::new(format_distance_from_now(
DateTimeType::Local(context.mtime),
false,
true,
true,
))
.color(Color::Muted)
.size(LabelSize::Small),
),
};
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(item),
)
}
}

View file

@ -78,7 +78,7 @@ zed_llm_client.workspace = true
[dev-dependencies]
agent_settings.workspace = true
assistant_context_editor.workspace = true
assistant_context.workspace = true
assistant_slash_command.workspace = true
async-trait.workspace = true
audio.workspace = true

View file

@ -6,7 +6,7 @@ use crate::{
},
};
use anyhow::{Result, anyhow};
use assistant_context_editor::ContextStore;
use assistant_context::ContextStore;
use assistant_slash_command::SlashCommandWorkingSet;
use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus, assert_hunks};
use call::{ActiveCall, ParticipantLocation, Room, room};

View file

@ -313,7 +313,7 @@ impl TestServer {
settings::KeymapFile::load_asset_allow_partial_failure(os_keymap, cx).unwrap(),
);
language_model::LanguageModelRegistry::test(cx);
assistant_context_editor::init(client.clone(), cx);
assistant_context::init(client.clone(), cx);
agent_settings::init(cx);
});

View file

@ -26,7 +26,6 @@ agent_settings.workspace = true
anyhow.workspace = true
askpass.workspace = true
assets.workspace = true
assistant_context_editor.workspace = true
assistant_tool.workspace = true
assistant_tools.workspace = true
audio.workspace = true

View file

@ -9,11 +9,10 @@ mod quick_action_bar;
#[cfg(target_os = "windows")]
pub(crate) mod windows_only_instance;
use agent_ui::AgentDiffToolbar;
use agent_ui::{AgentDiffToolbar, AgentPanelDelegate};
use anyhow::Context as _;
pub use app_menus::*;
use assets::Assets;
use assistant_context_editor::AgentPanelDelegate;
use breadcrumbs::Breadcrumbs;
use client::zed_urls;
use collections::VecDeque;