Standardize agent previews (#29790)
This PR makes agent previews render like any other preview in the component preview list & pages. Page:  List:  Release Notes: - N/A
This commit is contained in:
parent
225deb6785
commit
3bd7ae6e5b
@ -1211,7 +1211,7 @@ impl Component for MessageEditor {
|
||||
}
|
||||
|
||||
impl AgentPreview for MessageEditor {
|
||||
fn create_preview(
|
||||
fn agent_preview(
|
||||
workspace: WeakEntity<Workspace>,
|
||||
active_thread: Entity<ActiveThread>,
|
||||
thread_store: WeakEntity<ThreadStore>,
|
||||
|
@ -3,7 +3,7 @@ use component::ComponentId;
|
||||
use gpui::{App, Entity, WeakEntity};
|
||||
use linkme::distributed_slice;
|
||||
use std::sync::OnceLock;
|
||||
use ui::{AnyElement, Component, Window};
|
||||
use ui::{AnyElement, Component, ComponentScope, Window};
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::{ActiveThread, ThreadStore};
|
||||
@ -22,27 +22,20 @@ pub type PreviewFn = fn(
|
||||
pub static __ALL_AGENT_PREVIEWS: [fn() -> (ComponentId, PreviewFn)] = [..];
|
||||
|
||||
/// Trait that must be implemented by components that provide agent previews.
|
||||
pub trait AgentPreview: Component {
|
||||
/// Get the ID for this component
|
||||
///
|
||||
/// Eventually this will move to the component trait.
|
||||
fn id() -> ComponentId
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ComponentId(Self::name())
|
||||
pub trait AgentPreview: Component + Sized {
|
||||
#[allow(unused)] // We can't know this is used due to the distributed slice
|
||||
fn scope(&self) -> ComponentScope {
|
||||
ComponentScope::Agent
|
||||
}
|
||||
|
||||
/// Static method to create a preview for this component type
|
||||
fn create_preview(
|
||||
fn agent_preview(
|
||||
workspace: WeakEntity<Workspace>,
|
||||
active_thread: Entity<ActiveThread>,
|
||||
thread_store: WeakEntity<ThreadStore>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Option<AnyElement>
|
||||
where
|
||||
Self: Sized;
|
||||
) -> Option<AnyElement>;
|
||||
}
|
||||
|
||||
/// Register an agent preview for the given component type
|
||||
@ -55,8 +48,8 @@ macro_rules! register_agent_preview {
|
||||
$crate::ui::agent_preview::PreviewFn,
|
||||
) = || {
|
||||
(
|
||||
<$type as $crate::ui::agent_preview::AgentPreview>::id(),
|
||||
<$type as $crate::ui::agent_preview::AgentPreview>::create_preview,
|
||||
<$type as component::Component>::id(),
|
||||
<$type as $crate::ui::agent_preview::AgentPreview>::agent_preview,
|
||||
)
|
||||
};
|
||||
};
|
||||
|
@ -18,6 +18,9 @@ pub trait Component {
|
||||
fn name() -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
fn id() -> ComponentId {
|
||||
ComponentId(Self::name())
|
||||
}
|
||||
/// Returns a name that the component should be sorted by.
|
||||
///
|
||||
/// Implement this if the component should be sorted in an alternate order than its name.
|
||||
@ -81,7 +84,7 @@ pub fn register_component<T: Component>() {
|
||||
let component_data = (T::scope(), T::name(), T::sort_name(), T::description());
|
||||
let mut data = COMPONENT_DATA.write();
|
||||
data.components.push(component_data);
|
||||
data.previews.insert(T::name(), T::preview);
|
||||
data.previews.insert(T::id().0, T::preview);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -110,18 +110,7 @@ struct ComponentPreview {
|
||||
active_page: PreviewPage,
|
||||
components: Vec<ComponentMetadata>,
|
||||
component_list: ListState,
|
||||
agent_previews: Vec<
|
||||
Box<
|
||||
dyn Fn(
|
||||
&Self,
|
||||
WeakEntity<Workspace>,
|
||||
Entity<ActiveThread>,
|
||||
WeakEntity<ThreadStore>,
|
||||
&mut Window,
|
||||
&mut App,
|
||||
) -> Option<AnyElement>,
|
||||
>,
|
||||
>,
|
||||
agent_previews: Vec<ComponentId>,
|
||||
cursor_index: usize,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
@ -191,38 +180,7 @@ impl ComponentPreview {
|
||||
);
|
||||
|
||||
// Initialize agent previews
|
||||
let agent_previews = agent::all_agent_previews()
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
Box::new(
|
||||
move |_self: &ComponentPreview,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
active_thread: Entity<ActiveThread>,
|
||||
thread_store: WeakEntity<ThreadStore>,
|
||||
window: &mut Window,
|
||||
cx: &mut App| {
|
||||
agent::get_agent_preview(
|
||||
&id,
|
||||
workspace,
|
||||
active_thread,
|
||||
thread_store,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
},
|
||||
)
|
||||
as Box<
|
||||
dyn Fn(
|
||||
&ComponentPreview,
|
||||
WeakEntity<Workspace>,
|
||||
Entity<ActiveThread>,
|
||||
WeakEntity<ThreadStore>,
|
||||
&mut Window,
|
||||
&mut App,
|
||||
) -> Option<AnyElement>,
|
||||
>
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let agent_previews = agent::all_agent_previews();
|
||||
|
||||
let mut component_preview = Self {
|
||||
workspace_id: None,
|
||||
@ -635,44 +593,65 @@ impl ComponentPreview {
|
||||
|
||||
let description = component.description();
|
||||
|
||||
v_flex()
|
||||
.py_2()
|
||||
.child(
|
||||
v_flex()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_sm()
|
||||
.w_full()
|
||||
.gap_4()
|
||||
.py_4()
|
||||
.px_6()
|
||||
.flex_none()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex().gap_1().text_xl().child(div().child(name)).when(
|
||||
!matches!(scope, ComponentScope::None),
|
||||
|this| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
},
|
||||
),
|
||||
// Build the content container
|
||||
let mut preview_container = v_flex().py_2().child(
|
||||
v_flex()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_sm()
|
||||
.w_full()
|
||||
.gap_4()
|
||||
.py_4()
|
||||
.px_6()
|
||||
.flex_none()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.text_xl()
|
||||
.child(div().child(name))
|
||||
.when(!matches!(scope, ComponentScope::None), |this| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
}),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(component.preview(), |this, preview| {
|
||||
this.children(preview(window, cx))
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// Check if the component's scope is Agent
|
||||
if scope == ComponentScope::Agent {
|
||||
if let (Some(thread_store), Some(active_thread)) = (
|
||||
self.thread_store.as_ref().map(|ts| ts.downgrade()),
|
||||
self.active_thread.clone(),
|
||||
) {
|
||||
if let Some(element) = agent::get_agent_preview(
|
||||
&component.id(),
|
||||
self.workspace.clone(),
|
||||
active_thread,
|
||||
thread_store,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
preview_container = preview_container.child(element);
|
||||
} else if let Some(preview) = component.preview() {
|
||||
preview_container = preview_container.children(preview(window, cx));
|
||||
}
|
||||
}
|
||||
} else if let Some(preview) = component.preview() {
|
||||
preview_container = preview_container.children(preview(window, cx));
|
||||
}
|
||||
|
||||
preview_container.into_any_element()
|
||||
}
|
||||
|
||||
fn render_all_components(&self, cx: &Context<Self>) -> impl IntoElement {
|
||||
@ -711,7 +690,12 @@ impl ComponentPreview {
|
||||
v_flex()
|
||||
.id("render-component-page")
|
||||
.size_full()
|
||||
.child(ComponentPreviewPage::new(component.clone()))
|
||||
.child(ComponentPreviewPage::new(
|
||||
component.clone(),
|
||||
self.workspace.clone(),
|
||||
self.thread_store.as_ref().map(|ts| ts.downgrade()),
|
||||
self.active_thread.clone(),
|
||||
))
|
||||
.into_any_element()
|
||||
} else {
|
||||
v_flex()
|
||||
@ -732,13 +716,13 @@ impl ComponentPreview {
|
||||
.id("render-active-thread")
|
||||
.size_full()
|
||||
.child(
|
||||
v_flex().children(self.agent_previews.iter().filter_map(|preview_fn| {
|
||||
v_flex().children(self.agent_previews.iter().filter_map(|component_id| {
|
||||
if let (Some(thread_store), Some(active_thread)) = (
|
||||
self.thread_store.as_ref().map(|ts| ts.downgrade()),
|
||||
self.active_thread.clone(),
|
||||
) {
|
||||
preview_fn(
|
||||
self,
|
||||
agent::get_agent_preview(
|
||||
component_id,
|
||||
self.workspace.clone(),
|
||||
active_thread,
|
||||
thread_store,
|
||||
@ -894,7 +878,7 @@ impl Default for ActivePageId {
|
||||
|
||||
impl From<ComponentId> for ActivePageId {
|
||||
fn from(id: ComponentId) -> Self {
|
||||
ActivePageId(id.0.to_string())
|
||||
Self(id.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1073,16 +1057,25 @@ impl SerializableItem for ComponentPreview {
|
||||
pub struct ComponentPreviewPage {
|
||||
// languages: Arc<LanguageRegistry>,
|
||||
component: ComponentMetadata,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
thread_store: Option<WeakEntity<ThreadStore>>,
|
||||
active_thread: Option<Entity<ActiveThread>>,
|
||||
}
|
||||
|
||||
impl ComponentPreviewPage {
|
||||
pub fn new(
|
||||
component: ComponentMetadata,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
thread_store: Option<WeakEntity<ThreadStore>>,
|
||||
active_thread: Option<Entity<ActiveThread>>,
|
||||
// languages: Arc<LanguageRegistry>
|
||||
) -> Self {
|
||||
Self {
|
||||
// languages,
|
||||
component,
|
||||
workspace,
|
||||
thread_store,
|
||||
active_thread,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1113,12 +1106,32 @@ impl ComponentPreviewPage {
|
||||
}
|
||||
|
||||
fn render_preview(&self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
// Try to get agent preview first if we have an active thread
|
||||
let maybe_agent_preview = if let (Some(thread_store), Some(active_thread)) =
|
||||
(self.thread_store.as_ref(), self.active_thread.as_ref())
|
||||
{
|
||||
agent::get_agent_preview(
|
||||
&self.component.id(),
|
||||
self.workspace.clone(),
|
||||
active_thread.clone(),
|
||||
thread_store.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
v_flex()
|
||||
.flex_1()
|
||||
.px_12()
|
||||
.py_6()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(if let Some(preview) = self.component.preview() {
|
||||
.child(if let Some(element) = maybe_agent_preview {
|
||||
// Use agent preview if available
|
||||
element
|
||||
} else if let Some(preview) = self.component.preview() {
|
||||
// Fall back to component preview
|
||||
preview(window, cx).unwrap_or_else(|| {
|
||||
div()
|
||||
.child("Failed to load preview. This path should be unreachable")
|
||||
|
Loading…
x
Reference in New Issue
Block a user