Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T03:54:53.393Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

component.rs

326 lines · 9.5 KB · rust
1//! # Component
2//!
3//! This module provides the Component trait, which is used to define
4//! components for visual testing and debugging.
5//!
6//! Additionally, it includes layouts for rendering component examples
7//! and example groups, as well as the distributed slice mechanism for
8//! registering components.
9
10mod component_layout;
11
12use std::sync::LazyLock;
13
14pub use component_layout::*;
15
16use collections::HashMap;
17use gpui::{AnyElement, App, SharedString, Window};
18use parking_lot::RwLock;
19use strum::{Display, EnumString};
20
21pub fn components() -> ComponentRegistry {
22    COMPONENT_DATA.read().clone()
23}
24
25pub fn init() {
26    for f in inventory::iter::<ComponentFn>() {
27        (f.0)();
28    }
29}
30
31pub struct ComponentFn(fn());
32
33impl ComponentFn {
34    pub const fn new(f: fn()) -> Self {
35        Self(f)
36    }
37}
38
39inventory::collect!(ComponentFn);
40
41/// Private internals for macros.
42#[doc(hidden)]
43pub mod __private {
44    pub use inventory;
45}
46
47pub fn register_component<T: Component>() {
48    let id = T::id();
49    let metadata = ComponentMetadata {
50        id: id.clone(),
51        description: SharedString::new_static(T::description()),
52        name: SharedString::new_static(T::name()),
53        preview: T::preview,
54        scope: T::scope(),
55        sort_name: SharedString::new_static(T::sort_name()),
56        status: T::status(),
57    };
58
59    let mut data = COMPONENT_DATA.write();
60    data.components.insert(id, metadata);
61}
62
63pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
64    LazyLock::new(|| RwLock::new(ComponentRegistry::default()));
65
66#[derive(Default, Clone)]
67pub struct ComponentRegistry {
68    components: HashMap<ComponentId, ComponentMetadata>,
69}
70
71impl ComponentRegistry {
72    pub fn previews(&self) -> impl Iterator<Item = &ComponentMetadata> {
73        self.components.values()
74    }
75
76    pub fn sorted_previews(&self) -> Vec<ComponentMetadata> {
77        let mut previews: Vec<_> = self.previews().cloned().collect();
78        previews.sort_by_key(|a| a.name());
79        previews
80    }
81
82    pub fn components(&self) -> Vec<&ComponentMetadata> {
83        self.components.values().collect()
84    }
85
86    pub fn sorted_components(&self) -> Vec<ComponentMetadata> {
87        let mut components: Vec<ComponentMetadata> =
88            self.components().into_iter().cloned().collect();
89        components.sort_by_key(|a| a.name());
90        components
91    }
92
93    pub fn component_map(&self) -> HashMap<ComponentId, ComponentMetadata> {
94        self.components.clone()
95    }
96
97    pub fn get(&self, id: &ComponentId) -> Option<&ComponentMetadata> {
98        self.components.get(id)
99    }
100
101    pub fn len(&self) -> usize {
102        self.components.len()
103    }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Hash)]
107pub struct ComponentId(pub &'static str);
108
109#[derive(Clone)]
110pub struct ComponentMetadata {
111    id: ComponentId,
112    description: SharedString,
113    name: SharedString,
114    preview: fn(&mut Window, &mut App) -> AnyElement,
115    scope: ComponentScope,
116    sort_name: SharedString,
117    status: ComponentStatus,
118}
119
120impl ComponentMetadata {
121    pub fn id(&self) -> ComponentId {
122        self.id.clone()
123    }
124
125    pub fn description(&self) -> SharedString {
126        self.description.clone()
127    }
128
129    pub fn name(&self) -> SharedString {
130        self.name.clone()
131    }
132
133    pub fn preview(&self) -> fn(&mut Window, &mut App) -> AnyElement {
134        self.preview
135    }
136
137    pub fn scope(&self) -> ComponentScope {
138        self.scope.clone()
139    }
140
141    pub fn sort_name(&self) -> SharedString {
142        self.sort_name.clone()
143    }
144
145    pub fn scopeless_name(&self) -> SharedString {
146        self.name
147            .clone()
148            .split("::")
149            .last()
150            .unwrap_or(&self.name)
151            .to_string()
152            .into()
153    }
154
155    pub fn status(&self) -> ComponentStatus {
156        self.status.clone()
157    }
158}
159
160/// Implement this trait to define a UI component. This will allow you to
161/// derive `RegisterComponent` on it, in turn allowing you to preview the
162/// contents of the preview fn in `workspace: open component preview`.
163///
164/// This can be useful for visual debugging and testing, documenting UI
165/// patterns, or simply showing all the variants of a component.
166///
167/// Generally you will want to implement at least `scope` and `preview`
168/// from this trait, so you can preview the component, and it will show up
169/// in a section that makes sense.
170pub trait Component {
171    /// The component's unique identifier.
172    ///
173    /// Used to access previews, or state for more
174    /// complex, stateful components.
175    fn id() -> ComponentId {
176        ComponentId(Self::name())
177    }
178    /// Returns the scope of the component.
179    ///
180    /// This scope is used to determine how components and
181    /// their previews are displayed and organized.
182    fn scope() -> ComponentScope {
183        ComponentScope::None
184    }
185    /// The ready status of this component.
186    ///
187    /// Use this to mark when components are:
188    /// - `WorkInProgress`: Still being designed or are partially implemented.
189    /// - `EngineeringReady`: Ready to be implemented.
190    /// - `Deprecated`: No longer recommended for use.
191    ///
192    /// Defaults to [`Live`](ComponentStatus::Live).
193    fn status() -> ComponentStatus {
194        ComponentStatus::Live
195    }
196    /// The name of the component.
197    ///
198    /// This name is used to identify the component
199    /// and is usually derived from the component's type.
200    fn name() -> &'static str {
201        std::any::type_name::<Self>()
202    }
203    /// Returns a name that the component should be sorted by.
204    ///
205    /// Implement this if the component should be sorted in an alternate order than its name.
206    ///
207    /// Example:
208    ///
209    /// For example, to group related components together when sorted:
210    ///
211    /// - Button      -> ButtonA
212    /// - IconButton  -> ButtonBIcon
213    /// - ToggleButton -> ButtonCToggle
214    ///
215    /// This naming scheme keeps these components together and allows them to /// be sorted in a logical order.
216    fn sort_name() -> &'static str {
217        Self::name()
218    }
219    /// An optional description of the component.
220    ///
221    /// This will be displayed in the component's preview. To show a
222    /// component's doc comment as it's description, derive `Documented`.
223    ///
224    /// Example:
225    ///
226    /// ```
227    /// use documented::Documented;
228    ///
229    /// /// This is a doc comment.
230    /// #[derive(Documented)]
231    /// struct MyComponent;
232    ///
233    /// impl MyComponent {
234    ///     fn description() -> &'static str {
235    ///         Self::DOCS
236    ///     }
237    /// }
238    /// ```
239    ///
240    /// This will result in "This is a doc comment." being passed
241    /// to the component's description.
242    fn description() -> &'static str;
243    /// The component's preview.
244    ///
245    /// An element returned here will be shown in the component's preview.
246    ///
247    /// Useful component helpers:
248    /// - [`component::single_example`]
249    /// - [`component::component_group`]
250    /// - [`component::component_group_with_title`]
251    ///
252    /// Note: Any arbitrary element can be returned here.
253    ///
254    /// This is useful for displaying related UI to the component you are
255    /// trying to preview, such as a button that opens a modal or shows a
256    /// tooltip on hover, or a grid of icons showcasing all the icons available.
257    fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement;
258}
259
260/// The ready status of this component.
261///
262/// Use this to mark when components are:
263/// - `WorkInProgress`: Still being designed or are partially implemented.
264/// - `EngineeringReady`: Ready to be implemented.
265/// - `Deprecated`: No longer recommended for use.
266///
267/// Defaults to [`Live`](ComponentStatus::Live).
268#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, EnumString)]
269pub enum ComponentStatus {
270    #[strum(serialize = "Work In Progress")]
271    WorkInProgress,
272    #[strum(serialize = "Ready To Build")]
273    EngineeringReady,
274    Live,
275    Deprecated,
276}
277
278impl ComponentStatus {
279    pub fn description(&self) -> &str {
280        match self {
281            ComponentStatus::WorkInProgress => {
282                "These components are still being designed or refined. \
283                They shouldn't be used in the app yet."
284            }
285            ComponentStatus::EngineeringReady => {
286                "These components are design complete or partially implemented, \
287                and are ready for an engineer to complete their implementation."
288            }
289            ComponentStatus::Live => "These components are ready for use in the app.",
290            ComponentStatus::Deprecated => {
291                "These components are no longer recommended for use in the app, \
292                and may be removed in a future release."
293            }
294        }
295    }
296}
297
298#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, EnumString)]
299pub enum ComponentScope {
300    Agent,
301    Collaboration,
302    #[strum(serialize = "Data Display")]
303    DataDisplay,
304    Editor,
305    #[strum(serialize = "Images & Icons")]
306    Images,
307    #[strum(serialize = "Forms & Input")]
308    Input,
309    #[strum(serialize = "Layout & Structure")]
310    Layout,
311    #[strum(serialize = "Loading & Progress")]
312    Loading,
313    Navigation,
314    #[strum(serialize = "Unsorted")]
315    None,
316    Notification,
317    #[strum(serialize = "Overlays & Layering")]
318    Overlays,
319    Onboarding,
320    Status,
321    Typography,
322    Utilities,
323    #[strum(serialize = "Version Control")]
324    VersionControl,
325}
326
Served at tenant.openagents/omega Member data and write actions are omitted.