mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 02:46:43 +00:00
assistant: Extract ConfigurationView
to its own module (#23480)
This PR is a small refactoring that extracts the Assistant's `ConfigurationView` into its own module. Release Notes: - N/A
This commit is contained in:
parent
09fe1e7f66
commit
f7703973d2
3 changed files with 204 additions and 200 deletions
|
@ -1,5 +1,6 @@
|
||||||
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
|
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
|
||||||
|
|
||||||
|
mod assistant_configuration;
|
||||||
pub mod assistant_panel;
|
pub mod assistant_panel;
|
||||||
mod inline_assistant;
|
mod inline_assistant;
|
||||||
pub mod slash_command_settings;
|
pub mod slash_command_settings;
|
||||||
|
|
197
crates/assistant/src/assistant_configuration.rs
Normal file
197
crates/assistant/src/assistant_configuration.rs
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use collections::HashMap;
|
||||||
|
use gpui::{canvas, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Subscription};
|
||||||
|
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||||
|
use ui::{prelude::*, ElevationIndex};
|
||||||
|
use workspace::Item;
|
||||||
|
|
||||||
|
pub struct ConfigurationView {
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
configuration_views: HashMap<LanguageModelProviderId, AnyView>,
|
||||||
|
_registry_subscription: Subscription,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigurationView {
|
||||||
|
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let focus_handle = cx.focus_handle();
|
||||||
|
|
||||||
|
let registry_subscription = cx.subscribe(
|
||||||
|
&LanguageModelRegistry::global(cx),
|
||||||
|
|this, _, event: &language_model::Event, cx| match event {
|
||||||
|
language_model::Event::AddedProvider(provider_id) => {
|
||||||
|
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
|
||||||
|
if let Some(provider) = provider {
|
||||||
|
this.add_configuration_view(&provider, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
language_model::Event::RemovedProvider(provider_id) => {
|
||||||
|
this.remove_configuration_view(provider_id);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut this = Self {
|
||||||
|
focus_handle,
|
||||||
|
configuration_views: HashMap::default(),
|
||||||
|
_registry_subscription: registry_subscription,
|
||||||
|
};
|
||||||
|
this.build_configuration_views(cx);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
let providers = LanguageModelRegistry::read_global(cx).providers();
|
||||||
|
for provider in providers {
|
||||||
|
self.add_configuration_view(&provider, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
|
||||||
|
self.configuration_views.remove(provider_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_configuration_view(
|
||||||
|
&mut self,
|
||||||
|
provider: &Arc<dyn LanguageModelProvider>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
let configuration_view = provider.configuration_view(cx);
|
||||||
|
self.configuration_views
|
||||||
|
.insert(provider.id(), configuration_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_provider_view(
|
||||||
|
&mut self,
|
||||||
|
provider: &Arc<dyn LanguageModelProvider>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Div {
|
||||||
|
let provider_id = provider.id().0.clone();
|
||||||
|
let provider_name = provider.name().0.clone();
|
||||||
|
let configuration_view = self.configuration_views.get(&provider.id()).cloned();
|
||||||
|
|
||||||
|
let open_new_context = cx.listener({
|
||||||
|
let provider = provider.clone();
|
||||||
|
move |_, _, cx| {
|
||||||
|
cx.emit(ConfigurationViewEvent::NewProviderContextEditor(
|
||||||
|
provider.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.justify_between()
|
||||||
|
.child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
|
||||||
|
.when(provider.is_authenticated(cx), move |this| {
|
||||||
|
this.child(
|
||||||
|
h_flex().justify_end().child(
|
||||||
|
Button::new(
|
||||||
|
SharedString::from(format!("new-context-{provider_id}")),
|
||||||
|
"Open New Chat",
|
||||||
|
)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon(IconName::Plus)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::ModalSurface)
|
||||||
|
.on_click(open_new_context),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.p(DynamicSpacing::Base08.rems(cx))
|
||||||
|
.bg(cx.theme().colors().surface_background)
|
||||||
|
.border_1()
|
||||||
|
.border_color(cx.theme().colors().border_variant)
|
||||||
|
.rounded_md()
|
||||||
|
.when(configuration_view.is_none(), |this| {
|
||||||
|
this.child(div().child(Label::new(format!(
|
||||||
|
"No configuration view for {}",
|
||||||
|
provider_name
|
||||||
|
))))
|
||||||
|
})
|
||||||
|
.when_some(configuration_view, |this, configuration_view| {
|
||||||
|
this.child(configuration_view)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for ConfigurationView {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let providers = LanguageModelRegistry::read_global(cx).providers();
|
||||||
|
let provider_views = providers
|
||||||
|
.into_iter()
|
||||||
|
.map(|provider| self.render_provider_view(&provider, cx))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut element = v_flex()
|
||||||
|
.id("assistant-configuration-view")
|
||||||
|
.track_focus(&self.focus_handle(cx))
|
||||||
|
.bg(cx.theme().colors().editor_background)
|
||||||
|
.size_full()
|
||||||
|
.overflow_y_scroll()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.p(DynamicSpacing::Base16.rems(cx))
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.gap_1()
|
||||||
|
.child(Headline::new("Configure your Assistant").size(HeadlineSize::Medium))
|
||||||
|
.child(
|
||||||
|
Label::new(
|
||||||
|
"At least one LLM provider must be configured to use the Assistant.",
|
||||||
|
)
|
||||||
|
.color(Color::Muted),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.p(DynamicSpacing::Base16.rems(cx))
|
||||||
|
.mt_1()
|
||||||
|
.gap_6()
|
||||||
|
.flex_1()
|
||||||
|
.children(provider_views),
|
||||||
|
)
|
||||||
|
.into_any();
|
||||||
|
|
||||||
|
// We use a canvas here to get scrolling to work in the ConfigurationView. It's a workaround
|
||||||
|
// because we couldn't the element to take up the size of the parent.
|
||||||
|
canvas(
|
||||||
|
move |bounds, cx| {
|
||||||
|
element.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
|
||||||
|
element
|
||||||
|
},
|
||||||
|
|_, mut element, cx| {
|
||||||
|
element.paint(cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.flex_1()
|
||||||
|
.w_full()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ConfigurationViewEvent {
|
||||||
|
NewProviderContextEditor(Arc<dyn LanguageModelProvider>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<ConfigurationViewEvent> for ConfigurationView {}
|
||||||
|
|
||||||
|
impl FocusableView for ConfigurationView {
|
||||||
|
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item for ConfigurationView {
|
||||||
|
type Event = ConfigurationViewEvent;
|
||||||
|
|
||||||
|
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||||
|
Some("Configuration".into())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::assistant_configuration::{ConfigurationView, ConfigurationViewEvent};
|
||||||
use crate::{
|
use crate::{
|
||||||
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, DeployPromptLibrary,
|
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, DeployPromptLibrary,
|
||||||
InlineAssistant, NewContext,
|
InlineAssistant, NewContext,
|
||||||
|
@ -13,19 +14,15 @@ use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||||
use assistant_slash_command::SlashCommandWorkingSet;
|
use assistant_slash_command::SlashCommandWorkingSet;
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
use client::{proto, Client, Status};
|
use client::{proto, Client, Status};
|
||||||
use collections::HashMap;
|
|
||||||
use editor::{Editor, EditorEvent};
|
use editor::{Editor, EditorEvent};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
canvas, div, prelude::*, Action, AnyView, AppContext, AsyncWindowContext, EventEmitter,
|
prelude::*, Action, AppContext, AsyncWindowContext, EventEmitter, ExternalPaths, FocusHandle,
|
||||||
ExternalPaths, FocusHandle, FocusableView, InteractiveElement, IntoElement, Model,
|
FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Pixels, Render, Styled,
|
||||||
ParentElement, Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
Subscription, Task, UpdateGlobal, View, WeakView,
|
||||||
Task, UpdateGlobal, View, WeakView,
|
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use language_model::{
|
use language_model::{LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
|
||||||
LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID,
|
|
||||||
};
|
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::LanguageModelSelector;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
|
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
|
||||||
|
@ -34,12 +31,11 @@ use settings::{update_settings_file, Settings};
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{ops::ControlFlow, path::PathBuf, sync::Arc};
|
use std::{ops::ControlFlow, path::PathBuf, sync::Arc};
|
||||||
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
||||||
use ui::{prelude::*, ContextMenu, ElevationIndex, PopoverMenu, PopoverMenuHandle, Tooltip};
|
use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
||||||
use util::{maybe, ResultExt};
|
use util::{maybe, ResultExt};
|
||||||
use workspace::DraggedTab;
|
use workspace::DraggedTab;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
dock::{DockPosition, Panel, PanelEvent},
|
dock::{DockPosition, Panel, PanelEvent},
|
||||||
item::Item,
|
|
||||||
pane, DraggedSelection, Pane, ShowConfiguration, ToggleZoom, Workspace,
|
pane, DraggedSelection, Pane, ShowConfiguration, ToggleZoom, Workspace,
|
||||||
};
|
};
|
||||||
use zed_actions::assistant::{InlineAssist, ToggleFocus};
|
use zed_actions::assistant::{InlineAssist, ToggleFocus};
|
||||||
|
@ -1371,193 +1367,3 @@ pub enum WorkflowAssistStatus {
|
||||||
Done,
|
Done,
|
||||||
Idle,
|
Idle,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConfigurationView {
|
|
||||||
focus_handle: FocusHandle,
|
|
||||||
configuration_views: HashMap<LanguageModelProviderId, AnyView>,
|
|
||||||
_registry_subscription: Subscription,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigurationView {
|
|
||||||
fn new(cx: &mut ViewContext<Self>) -> Self {
|
|
||||||
let focus_handle = cx.focus_handle();
|
|
||||||
|
|
||||||
let registry_subscription = cx.subscribe(
|
|
||||||
&LanguageModelRegistry::global(cx),
|
|
||||||
|this, _, event: &language_model::Event, cx| match event {
|
|
||||||
language_model::Event::AddedProvider(provider_id) => {
|
|
||||||
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
|
|
||||||
if let Some(provider) = provider {
|
|
||||||
this.add_configuration_view(&provider, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
language_model::Event::RemovedProvider(provider_id) => {
|
|
||||||
this.remove_configuration_view(provider_id);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut this = Self {
|
|
||||||
focus_handle,
|
|
||||||
configuration_views: HashMap::default(),
|
|
||||||
_registry_subscription: registry_subscription,
|
|
||||||
};
|
|
||||||
this.build_configuration_views(cx);
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
|
|
||||||
let providers = LanguageModelRegistry::read_global(cx).providers();
|
|
||||||
for provider in providers {
|
|
||||||
self.add_configuration_view(&provider, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
|
|
||||||
self.configuration_views.remove(provider_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_configuration_view(
|
|
||||||
&mut self,
|
|
||||||
provider: &Arc<dyn LanguageModelProvider>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) {
|
|
||||||
let configuration_view = provider.configuration_view(cx);
|
|
||||||
self.configuration_views
|
|
||||||
.insert(provider.id(), configuration_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_provider_view(
|
|
||||||
&mut self,
|
|
||||||
provider: &Arc<dyn LanguageModelProvider>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Div {
|
|
||||||
let provider_id = provider.id().0.clone();
|
|
||||||
let provider_name = provider.name().0.clone();
|
|
||||||
let configuration_view = self.configuration_views.get(&provider.id()).cloned();
|
|
||||||
|
|
||||||
let open_new_context = cx.listener({
|
|
||||||
let provider = provider.clone();
|
|
||||||
move |_, _, cx| {
|
|
||||||
cx.emit(ConfigurationViewEvent::NewProviderContextEditor(
|
|
||||||
provider.clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
v_flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.justify_between()
|
|
||||||
.child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
|
|
||||||
.when(provider.is_authenticated(cx), move |this| {
|
|
||||||
this.child(
|
|
||||||
h_flex().justify_end().child(
|
|
||||||
Button::new(
|
|
||||||
SharedString::from(format!("new-context-{provider_id}")),
|
|
||||||
"Open New Chat",
|
|
||||||
)
|
|
||||||
.icon_position(IconPosition::Start)
|
|
||||||
.icon(IconName::Plus)
|
|
||||||
.style(ButtonStyle::Filled)
|
|
||||||
.layer(ElevationIndex::ModalSurface)
|
|
||||||
.on_click(open_new_context),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.p(DynamicSpacing::Base08.rems(cx))
|
|
||||||
.bg(cx.theme().colors().surface_background)
|
|
||||||
.border_1()
|
|
||||||
.border_color(cx.theme().colors().border_variant)
|
|
||||||
.rounded_md()
|
|
||||||
.when(configuration_view.is_none(), |this| {
|
|
||||||
this.child(div().child(Label::new(format!(
|
|
||||||
"No configuration view for {}",
|
|
||||||
provider_name
|
|
||||||
))))
|
|
||||||
})
|
|
||||||
.when_some(configuration_view, |this, configuration_view| {
|
|
||||||
this.child(configuration_view)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for ConfigurationView {
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
|
||||||
let providers = LanguageModelRegistry::read_global(cx).providers();
|
|
||||||
let provider_views = providers
|
|
||||||
.into_iter()
|
|
||||||
.map(|provider| self.render_provider_view(&provider, cx))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut element = v_flex()
|
|
||||||
.id("assistant-configuration-view")
|
|
||||||
.track_focus(&self.focus_handle(cx))
|
|
||||||
.bg(cx.theme().colors().editor_background)
|
|
||||||
.size_full()
|
|
||||||
.overflow_y_scroll()
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.p(DynamicSpacing::Base16.rems(cx))
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.gap_1()
|
|
||||||
.child(Headline::new("Configure your Assistant").size(HeadlineSize::Medium))
|
|
||||||
.child(
|
|
||||||
Label::new(
|
|
||||||
"At least one LLM provider must be configured to use the Assistant.",
|
|
||||||
)
|
|
||||||
.color(Color::Muted),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.p(DynamicSpacing::Base16.rems(cx))
|
|
||||||
.mt_1()
|
|
||||||
.gap_6()
|
|
||||||
.flex_1()
|
|
||||||
.children(provider_views),
|
|
||||||
)
|
|
||||||
.into_any();
|
|
||||||
|
|
||||||
// We use a canvas here to get scrolling to work in the ConfigurationView. It's a workaround
|
|
||||||
// because we couldn't the element to take up the size of the parent.
|
|
||||||
canvas(
|
|
||||||
move |bounds, cx| {
|
|
||||||
element.prepaint_as_root(bounds.origin, bounds.size.into(), cx);
|
|
||||||
element
|
|
||||||
},
|
|
||||||
|_, mut element, cx| {
|
|
||||||
element.paint(cx);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ConfigurationViewEvent {
|
|
||||||
NewProviderContextEditor(Arc<dyn LanguageModelProvider>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventEmitter<ConfigurationViewEvent> for ConfigurationView {}
|
|
||||||
|
|
||||||
impl FocusableView for ConfigurationView {
|
|
||||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
|
||||||
self.focus_handle.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Item for ConfigurationView {
|
|
||||||
type Event = ConfigurationViewEvent;
|
|
||||||
|
|
||||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
|
||||||
Some("Configuration".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue