Skip to repository content

tenant.openagents/omega

No repository description is available.

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

multibuffer_hint.rs

188 lines · 6.1 KB · rust
1use std::collections::HashSet;
2use std::sync::OnceLock;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use db::kvp::KeyValueStore;
6use gpui::{App, EntityId, EventEmitter, Subscription};
7use ui::{IconButtonShape, Tooltip, prelude::*};
8use workspace::item::{ItemBufferKind, ItemEvent, ItemHandle};
9use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
10
11pub struct MultibufferHint {
12    shown_on: HashSet<EntityId>,
13    active_item: Option<Box<dyn ItemHandle>>,
14    subscription: Option<Subscription>,
15}
16
17const NUMBER_OF_HINTS: usize = 10;
18
19const SHOWN_COUNT_KEY: &str = "MULTIBUFFER_HINT_SHOWN_COUNT";
20
21impl Default for MultibufferHint {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl MultibufferHint {
28    pub fn new() -> Self {
29        Self {
30            shown_on: Default::default(),
31            active_item: None,
32            subscription: None,
33        }
34    }
35}
36
37impl MultibufferHint {
38    fn counter(cx: &App) -> &'static AtomicUsize {
39        static SHOWN_COUNT: OnceLock<AtomicUsize> = OnceLock::new();
40        SHOWN_COUNT.get_or_init(|| {
41            let value: usize = KeyValueStore::global(cx)
42                .read_kvp(SHOWN_COUNT_KEY)
43                .ok()
44                .flatten()
45                .and_then(|v| v.parse().ok())
46                .unwrap_or(0);
47
48            AtomicUsize::new(value)
49        })
50    }
51
52    fn shown_count(cx: &App) -> usize {
53        Self::counter(cx).load(Ordering::Relaxed)
54    }
55
56    fn increment_count(cx: &mut App) {
57        Self::set_count(Self::shown_count(cx) + 1, cx)
58    }
59
60    pub(crate) fn set_count(count: usize, cx: &mut App) {
61        Self::counter(cx).store(count, Ordering::Relaxed);
62
63        let kvp = KeyValueStore::global(cx);
64        db::write_and_log(cx, move || async move {
65            kvp.write_kvp(SHOWN_COUNT_KEY.to_string(), format!("{}", count))
66                .await
67        });
68    }
69
70    fn dismiss(&mut self, cx: &mut App) {
71        Self::set_count(NUMBER_OF_HINTS, cx)
72    }
73
74    /// Determines the toolbar location for this [`MultibufferHint`].
75    fn determine_toolbar_location(&mut self, cx: &mut Context<Self>) -> ToolbarItemLocation {
76        if Self::shown_count(cx) >= NUMBER_OF_HINTS {
77            return ToolbarItemLocation::Hidden;
78        }
79
80        let Some(active_pane_item) = self.active_item.as_ref() else {
81            return ToolbarItemLocation::Hidden;
82        };
83
84        if active_pane_item.buffer_kind(cx) == ItemBufferKind::Singleton
85            || active_pane_item.breadcrumbs(cx).is_none()
86            || !active_pane_item.can_save(cx)
87        {
88            return ToolbarItemLocation::Hidden;
89        }
90
91        if self.shown_on.insert(active_pane_item.item_id()) {
92            Self::increment_count(cx);
93        }
94
95        ToolbarItemLocation::Secondary
96    }
97}
98
99impl EventEmitter<ToolbarItemEvent> for MultibufferHint {}
100
101impl ToolbarItemView for MultibufferHint {
102    fn set_active_pane_item(
103        &mut self,
104        active_pane_item: Option<&dyn ItemHandle>,
105        window: &mut Window,
106        cx: &mut Context<Self>,
107    ) -> ToolbarItemLocation {
108        cx.notify();
109        self.active_item = active_pane_item.map(|item| item.boxed_clone());
110
111        let Some(active_pane_item) = active_pane_item else {
112            return ToolbarItemLocation::Hidden;
113        };
114
115        let this = cx.entity().downgrade();
116        self.subscription = Some(active_pane_item.subscribe_to_item_events(
117            window,
118            cx,
119            Box::new(move |event, _, cx| {
120                if let ItemEvent::UpdateBreadcrumbs = event {
121                    this.update(cx, |this, cx| {
122                        cx.notify();
123                        let location = this.determine_toolbar_location(cx);
124                        cx.emit(ToolbarItemEvent::ChangeLocation(location))
125                    })
126                    .ok();
127                }
128            }),
129        ));
130
131        self.determine_toolbar_location(cx)
132    }
133}
134
135impl Render for MultibufferHint {
136    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
137        h_flex()
138            .px_2()
139            .py_0p5()
140            .justify_between()
141            .bg(cx.theme().status().info_background.opacity(0.5))
142            .border_1()
143            .border_color(cx.theme().colors().border_variant)
144            .rounded_sm()
145            .overflow_hidden()
146            .child(
147                h_flex()
148                    .gap_0p5()
149                    .child(
150                        h_flex()
151                            .gap_2()
152                            .child(
153                                Icon::new(IconName::Info)
154                                    .size(IconSize::XSmall)
155                                    .color(Color::Muted),
156                            )
157                            .child(Label::new(
158                                "Edit and save files directly in the results multibuffer!",
159                            )),
160                    )
161                    .child(
162                        Button::new("open_docs", "Learn More")
163                            .end_icon(
164                                Icon::new(IconName::ArrowUpRight)
165                                    .size(IconSize::Small)
166                                    .color(Color::Muted),
167                            )
168                            .on_click(move |_event, _, cx| {
169                                cx.open_url("https://github.com/OpenAgentsInc/omega#readme")
170                            }),
171                    ),
172            )
173            .child(
174                IconButton::new("dismiss", IconName::Close)
175                    .shape(IconButtonShape::Square)
176                    .icon_size(IconSize::Small)
177                    .on_click(cx.listener(|this, _event, _, cx| {
178                        this.dismiss(cx);
179                        cx.emit(ToolbarItemEvent::ChangeLocation(
180                            ToolbarItemLocation::Hidden,
181                        ))
182                    }))
183                    .tooltip(Tooltip::text("Dismiss Hint")),
184            )
185            .into_any_element()
186    }
187}
188
Served at tenant.openagents/omega Member data and write actions are omitted.