Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T03:35:23.416Z 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

facepile.rs

133 lines · 4.0 KB · rust
1use crate::component_prelude::*;
2use crate::prelude::*;
3use gpui::{AnyElement, StyleRefinement};
4use smallvec::SmallVec;
5
6use super::Avatar;
7
8/// An element that displays a collection of (usually) faces stacked
9/// horizontally, with the left-most face on top, visually descending
10/// from left to right.
11///
12/// Facepiles are used to display a group of people or things,
13/// such as a list of participants in a collaboration session.
14///
15/// # Examples
16///
17/// ## Default
18///
19/// A default, horizontal facepile.
20///
21/// ```
22/// use gpui::IntoElement;
23/// use ui::{Avatar, Facepile, EXAMPLE_FACES};
24///
25/// let facepile = Facepile::new(
26///     EXAMPLE_FACES.iter().take(3).map(|&url|
27///         Avatar::new(url).into_any_element()).collect()
28/// );
29/// ```
30#[derive(IntoElement, Documented, RegisterComponent)]
31pub struct Facepile {
32    base: Div,
33    faces: SmallVec<[AnyElement; 2]>,
34}
35
36impl Facepile {
37    /// Creates a new empty facepile.
38    pub fn empty() -> Self {
39        Self::new(SmallVec::new())
40    }
41
42    /// Creates a new facepile with the given faces.
43    pub fn new(faces: SmallVec<[AnyElement; 2]>) -> Self {
44        Self { base: div(), faces }
45    }
46}
47
48impl ParentElement for Facepile {
49    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
50        self.faces.extend(elements);
51    }
52}
53
54// Style methods.
55impl Facepile {
56    fn style(&mut self) -> &mut StyleRefinement {
57        self.base.style()
58    }
59
60    gpui::padding_style_methods!({
61        visibility: pub
62    });
63}
64
65impl RenderOnce for Facepile {
66    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
67        // Lay the faces out in reverse so they overlap in the desired order (left to right, front to back)
68        self.base
69            .flex()
70            .flex_row_reverse()
71            .items_center()
72            .justify_start()
73            .children(
74                self.faces
75                    .into_iter()
76                    .enumerate()
77                    .rev()
78                    .map(|(ix, player)| div().when(ix > 0, |div| div.ml_neg_1()).child(player)),
79            )
80    }
81}
82
83pub const EXAMPLE_FACES: [&str; 6] = [
84    "https://avatars.githubusercontent.com/u/326587?s=60&v=4",
85    "https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
86    "https://avatars.githubusercontent.com/u/1789?s=60&v=4",
87    "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
88    "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
89    "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
90];
91
92impl Component for Facepile {
93    fn scope() -> ComponentScope {
94        ComponentScope::Collaboration
95    }
96
97    fn description() -> &'static str {
98        "Displays a collection of avatars or initials in a compact format. \
99        Often used to represent active collaborators or a subset of contributors."
100    }
101
102    fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
103        v_flex()
104            .gap_6()
105            .children(vec![example_group_with_title(
106                "Facepile Examples",
107                vec![
108                    single_example(
109                        "Default",
110                        Facepile::new(
111                            EXAMPLE_FACES
112                                .iter()
113                                .map(|&url| Avatar::new(url).into_any_element())
114                                .collect(),
115                        )
116                        .into_any_element(),
117                    ),
118                    single_example(
119                        "Custom Size",
120                        Facepile::new(
121                            EXAMPLE_FACES
122                                .iter()
123                                .map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
124                                .collect(),
125                        )
126                        .into_any_element(),
127                    ),
128                ],
129            )])
130            .into_any_element()
131    }
132}
133
Served at tenant.openagents/omega Member data and write actions are omitted.