Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:36:21.309Z 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

tooltip.rs

292 lines · 8.3 KB · rust
1use std::borrow::Borrow;
2use std::rc::Rc;
3
4use crate::prelude::*;
5use crate::{Color, KeyBinding, Label, LabelSize, StyledExt, h_flex, v_flex};
6use gpui::{Action, AnyElement, AnyView, AppContext, FocusHandle, IntoElement, Render};
7
8#[derive(RegisterComponent)]
9pub struct Tooltip {
10    title: Title,
11    meta: Option<SharedString>,
12    key_binding: Option<KeyBinding>,
13}
14
15#[derive(Clone, IntoElement)]
16enum Title {
17    Str(SharedString),
18    Callback(Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>),
19}
20
21impl From<SharedString> for Title {
22    fn from(value: SharedString) -> Self {
23        Title::Str(value)
24    }
25}
26
27impl RenderOnce for Title {
28    fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement {
29        match self {
30            Title::Str(title) => title.into_any_element(),
31            Title::Callback(element) => element(window, cx),
32        }
33    }
34}
35
36impl Tooltip {
37    pub fn simple(title: impl Into<SharedString>, cx: &mut App) -> AnyView {
38        cx.new(|_| Self {
39            title: Title::Str(title.into()),
40            meta: None,
41            key_binding: None,
42        })
43        .into()
44    }
45
46    pub fn text(title: impl Into<SharedString>) -> impl Fn(&mut Window, &mut App) -> AnyView {
47        let title = title.into();
48        move |_, cx| {
49            cx.new(|_| Self {
50                title: title.clone().into(),
51                meta: None,
52                key_binding: None,
53            })
54            .into()
55        }
56    }
57
58    pub fn for_action_title<T: Into<SharedString>>(
59        title: T,
60        action: &dyn Action,
61    ) -> impl Fn(&mut Window, &mut App) -> AnyView + use<T> {
62        let title = title.into();
63        let action = action.boxed_clone();
64        move |_, cx| {
65            cx.new(|cx| Self {
66                title: Title::Str(title.clone()),
67                meta: None,
68                key_binding: Some(KeyBinding::for_action(action.as_ref(), cx)),
69            })
70            .into()
71        }
72    }
73
74    pub fn for_action_title_in<Str: Into<SharedString>>(
75        title: Str,
76        action: &dyn Action,
77        focus_handle: &FocusHandle,
78    ) -> impl Fn(&mut Window, &mut App) -> AnyView + use<Str> {
79        let title = title.into();
80        let action = action.boxed_clone();
81        let focus_handle = focus_handle.clone();
82        move |_, cx| {
83            cx.new(|cx| Self {
84                title: Title::Str(title.clone()),
85                meta: None,
86                key_binding: Some(KeyBinding::for_action_in(
87                    action.as_ref(),
88                    &focus_handle,
89                    cx,
90                )),
91            })
92            .into()
93        }
94    }
95
96    pub fn for_action(
97        title: impl Into<SharedString>,
98        action: &dyn Action,
99        cx: &mut App,
100    ) -> AnyView {
101        cx.new(|cx| Self {
102            title: Title::Str(title.into()),
103            meta: None,
104            key_binding: Some(KeyBinding::for_action(action, cx)),
105        })
106        .into()
107    }
108
109    pub fn for_action_in(
110        title: impl Into<SharedString>,
111        action: &dyn Action,
112        focus_handle: &FocusHandle,
113        cx: &mut App,
114    ) -> AnyView {
115        cx.new(|cx| Self {
116            title: title.into().into(),
117            meta: None,
118            key_binding: Some(KeyBinding::for_action_in(action, focus_handle, cx)),
119        })
120        .into()
121    }
122
123    pub fn with_meta(
124        title: impl Into<SharedString>,
125        action: Option<&dyn Action>,
126        meta: impl Into<SharedString>,
127        cx: &mut App,
128    ) -> AnyView {
129        cx.new(|cx| Self {
130            title: title.into().into(),
131            meta: Some(meta.into()),
132            key_binding: action.map(|action| KeyBinding::for_action(action, cx)),
133        })
134        .into()
135    }
136
137    pub fn with_meta_in(
138        title: impl Into<SharedString>,
139        action: Option<&dyn Action>,
140        meta: impl Into<SharedString>,
141        focus_handle: &FocusHandle,
142        cx: &mut App,
143    ) -> AnyView {
144        cx.new(|cx| Self {
145            title: title.into().into(),
146            meta: Some(meta.into()),
147            key_binding: action.map(|action| KeyBinding::for_action_in(action, focus_handle, cx)),
148        })
149        .into()
150    }
151
152    pub fn new(title: impl Into<SharedString>) -> Self {
153        Self {
154            title: title.into().into(),
155            meta: None,
156            key_binding: None,
157        }
158    }
159
160    pub fn new_element(title: impl Fn(&mut Window, &mut App) -> AnyElement + 'static) -> Self {
161        Self {
162            title: Title::Callback(Rc::new(title)),
163            meta: None,
164            key_binding: None,
165        }
166    }
167
168    pub fn element(
169        title: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
170    ) -> impl Fn(&mut Window, &mut App) -> AnyView {
171        let title = Title::Callback(Rc::new(title));
172        move |_, cx| {
173            let title = title.clone();
174            cx.new(|_| Self {
175                title,
176                meta: None,
177                key_binding: None,
178            })
179            .into()
180        }
181    }
182
183    pub fn meta(mut self, meta: impl Into<SharedString>) -> Self {
184        self.meta = Some(meta.into());
185        self
186    }
187
188    pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
189        self.key_binding = key_binding.into();
190        self
191    }
192}
193
194impl Render for Tooltip {
195    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
196        tooltip_container(cx, |el, _| {
197            el.child(
198                h_flex()
199                    .gap_4()
200                    .child(div().max_w_72().child(self.title.clone()))
201                    .when_some(self.key_binding.clone(), |this, key_binding| {
202                        this.justify_between().child(key_binding)
203                    }),
204            )
205            .when_some(self.meta.clone(), |this, meta| {
206                this.child(
207                    div()
208                        .max_w_72()
209                        .child(Label::new(meta).size(LabelSize::Small).color(Color::Muted)),
210                )
211            })
212        })
213    }
214}
215
216pub fn tooltip_container<C>(cx: &mut C, f: impl FnOnce(Div, &mut C) -> Div) -> impl IntoElement
217where
218    C: AppContext + Borrow<App>,
219{
220    let app = (*cx).borrow();
221    let ui_font = theme::theme_settings(app).ui_font(app).clone();
222
223    // padding to avoid tooltip appearing right below the mouse cursor
224    div().pl_2().pt_2p5().child(
225        v_flex()
226            .elevation_2(app)
227            .font(ui_font)
228            .text_ui(app)
229            .text_color(app.theme().colors().text)
230            .py_1()
231            .px_2()
232            .map(|el| f(el, cx)),
233    )
234}
235
236pub struct LinkPreview {
237    link: SharedString,
238}
239
240impl LinkPreview {
241    pub fn new(url: &str, cx: &mut App) -> AnyView {
242        let mut wrapped_url = String::new();
243        for (i, ch) in url.chars().enumerate() {
244            if i == 500 {
245                wrapped_url.push('…');
246                break;
247            }
248            if i % 100 == 0 && i != 0 {
249                wrapped_url.push('\n');
250            }
251            wrapped_url.push(ch);
252        }
253        cx.new(|_| LinkPreview {
254            link: wrapped_url.into(),
255        })
256        .into()
257    }
258}
259
260impl Render for LinkPreview {
261    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
262        tooltip_container(cx, |el, _| {
263            el.child(
264                Label::new(self.link.clone())
265                    .size(LabelSize::XSmall)
266                    .color(Color::Muted),
267            )
268        })
269    }
270}
271
272impl Component for Tooltip {
273    fn scope() -> ComponentScope {
274        ComponentScope::DataDisplay
275    }
276
277    fn description() -> &'static str {
278        "A tooltip that appears when hovering over an element, \
279        optionally showing a keybinding or additional metadata."
280    }
281
282    fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
283        example_group(vec![single_example(
284            "Text only",
285            Button::new("delete-example", "Delete")
286                .tooltip(Tooltip::text("This is a tooltip!"))
287                .into_any_element(),
288        )])
289        .into_any_element()
290    }
291}
292
Served at tenant.openagents/omega Member data and write actions are omitted.