Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:32:43.134Z 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

mode_indicator.rs

197 lines · 7.4 KB · rust
1use gpui::{
2    App, Context, Element, Entity, FontWeight, Render, Subscription, WeakEntity, Window, div,
3};
4use ui::text_for_keystrokes;
5use workspace::{HideStatusItem, StatusItemView, item::ItemHandle, ui::prelude::*};
6
7use crate::{Vim, VimEvent, VimGlobals};
8
9/// The ModeIndicator displays the current mode in the status bar.
10pub struct ModeIndicator {
11    vim: Option<WeakEntity<Vim>>,
12    pending_keys: Option<String>,
13    vim_subscription: Option<Subscription>,
14}
15
16impl ModeIndicator {
17    /// Construct a new mode indicator in this window.
18    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
19        cx.observe_pending_input(window, |this: &mut Self, window, cx| {
20            this.update_pending_keys(window, cx);
21            cx.notify();
22        })
23        .detach();
24
25        let handle = cx.entity();
26        let window_handle = window.window_handle();
27        cx.observe_new::<Vim>(move |_, window, cx| {
28            let Some(window) = window else {
29                return;
30            };
31            if window.window_handle() != window_handle {
32                return;
33            }
34            let vim = cx.entity();
35            handle.update(cx, |_, cx| {
36                cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event {
37                    VimEvent::Focused => {
38                        mode_indicator.vim_subscription =
39                            Some(cx.observe(&vim, |_, _, cx| cx.notify()));
40                        mode_indicator.vim = Some(vim.downgrade());
41                    }
42                })
43                .detach()
44            })
45        })
46        .detach();
47
48        Self {
49            vim: None,
50            pending_keys: None,
51            vim_subscription: None,
52        }
53    }
54
55    fn update_pending_keys(&mut self, window: &mut Window, cx: &App) {
56        self.pending_keys = window
57            .pending_input_keystrokes()
58            .map(|keystrokes| text_for_keystrokes(keystrokes, cx));
59    }
60
61    fn vim(&self) -> Option<Entity<Vim>> {
62        self.vim.as_ref().and_then(|vim| vim.upgrade())
63    }
64
65    fn current_operators_description(&self, vim: Entity<Vim>, cx: &mut Context<Self>) -> String {
66        let recording = Vim::globals(cx)
67            .recording_register
68            .map(|reg| format!("recording @{reg} "))
69            .into_iter();
70
71        let vim = vim.read(cx);
72        recording
73            .chain(
74                cx.global::<VimGlobals>()
75                    .pre_count
76                    .map(|count| format!("{}", count)),
77            )
78            .chain(vim.selected_register.map(|reg| format!("\"{reg}")))
79            .chain(vim.operator_stack.iter().map(|item| item.status()))
80            .chain(
81                cx.global::<VimGlobals>()
82                    .post_count
83                    .map(|count| format!("{}", count)),
84            )
85            .collect::<Vec<_>>()
86            .concat()
87    }
88}
89
90impl Render for ModeIndicator {
91    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
92        let vim = self.vim();
93        let Some(vim) = vim else {
94            return div().hidden().into_any_element();
95        };
96
97        let vim_readable = vim.read(cx);
98        let status_label = vim_readable.status_label.clone();
99        let temp_mode = vim_readable.temp_mode;
100        let mode = vim_readable.mode;
101
102        let theme = cx.theme();
103        let colors = theme.colors();
104        let system_transparent = gpui::hsla(0.0, 0.0, 0.0, 0.0);
105        let vim_mode_text = match mode {
106            crate::state::Mode::Normal => colors.vim_normal_foreground,
107            crate::state::Mode::Insert => colors.vim_insert_foreground,
108            crate::state::Mode::Replace => colors.vim_replace_foreground,
109            crate::state::Mode::Visual => colors.vim_visual_foreground,
110            crate::state::Mode::VisualLine => colors.vim_visual_line_foreground,
111            crate::state::Mode::VisualBlock => colors.vim_visual_block_foreground,
112            crate::state::Mode::HelixNormal => colors.vim_helix_normal_foreground,
113            crate::state::Mode::HelixSelect => colors.vim_helix_select_foreground,
114        };
115        let bg_color = match mode {
116            crate::state::Mode::Normal => colors.vim_normal_background,
117            crate::state::Mode::Insert => colors.vim_insert_background,
118            crate::state::Mode::Replace => colors.vim_replace_background,
119            crate::state::Mode::Visual => colors.vim_visual_background,
120            crate::state::Mode::VisualLine => colors.vim_visual_line_background,
121            crate::state::Mode::VisualBlock => colors.vim_visual_block_background,
122            crate::state::Mode::HelixNormal => colors.vim_helix_normal_background,
123            crate::state::Mode::HelixSelect => colors.vim_helix_select_background,
124        };
125
126        let (label, mode): (SharedString, Option<SharedString>) = if let Some(label) = status_label
127        {
128            (label, None)
129        } else {
130            let mode_str = if temp_mode {
131                format!("(insert) {}", mode)
132            } else {
133                mode.to_string()
134            };
135
136            let current_operators_description = self.current_operators_description(vim.clone(), cx);
137            let pending = self
138                .pending_keys
139                .as_ref()
140                .unwrap_or(&current_operators_description);
141            let mode = if bg_color != system_transparent {
142                mode_str.into()
143            } else {
144                format!("-- {} --", mode_str).into()
145            };
146            (pending.into(), Some(mode))
147        };
148        h_flex()
149            .gap_1()
150            .when(!label.is_empty(), |el| {
151                el.child(
152                    Label::new(label)
153                        .line_height_style(LineHeightStyle::UiLabel)
154                        .weight(FontWeight::MEDIUM),
155                )
156            })
157            .when_some(mode, |el, mode| {
158                el.child(
159                    v_flex()
160                        .when(bg_color != system_transparent, |el| el.px_2())
161                        // match with other icons at the bottom that use default buttons
162                        .h(ButtonSize::Default.rems())
163                        .justify_center()
164                        .rounded_sm()
165                        .bg(bg_color)
166                        .child(
167                            Label::new(mode)
168                                .size(LabelSize::Small)
169                                .line_height_style(LineHeightStyle::UiLabel)
170                                .weight(FontWeight::MEDIUM)
171                                .when(
172                                    bg_color != system_transparent
173                                        && vim_mode_text != system_transparent,
174                                    |el| el.color(Color::Custom(vim_mode_text)),
175                                ),
176                        ),
177                )
178            })
179            .into_any()
180    }
181}
182
183impl StatusItemView for ModeIndicator {
184    fn set_active_pane_item(
185        &mut self,
186        _active_pane_item: Option<&dyn ItemHandle>,
187        _window: &mut Window,
188        _cx: &mut Context<Self>,
189    ) {
190    }
191
192    fn hide_setting(&self, _: &App) -> Option<HideStatusItem> {
193        // The Vim mode indicator is only visible while Vim mode is on.
194        None
195    }
196}
197
Served at tenant.openagents/omega Member data and write actions are omitted.