Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T06:53:14.905Z 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

quick_action_bar.rs

793 lines · 38.6 KB · rust
1mod preview;
2mod repl_menu;
3
4use agent_settings::AgentSettings;
5use editor::actions::{
6    AddSelectionAbove, AddSelectionBelow, CodeActionSource, DuplicateLineDown, GoToDiagnostic,
7    GoToHunk, GoToPreviousDiagnostic, GoToPreviousHunk, MoveLineDown, MoveLineUp, SelectAll,
8    SelectLargerSyntaxNode, SelectNext, SelectSmallerSyntaxNode, ToggleCodeActions,
9    ToggleDiagnostics, ToggleGoToLine, ToggleInlineDiagnostics,
10};
11use editor::code_context_menus::{CodeContextMenu, ContextMenuOrigin};
12use editor::{Editor, EditorSettings};
13use gpui::{
14    Action, Anchor, AnchoredPositionMode, ClickEvent, Context, ElementId, Entity, EventEmitter,
15    FocusHandle, Focusable, InteractiveElement, ParentElement, Render, Styled, Subscription,
16    WeakEntity, Window, anchored, deferred, point,
17};
18use project::{DisableAiSettings, project_settings::DiagnosticSeverity};
19use search::{BufferSearchBar, buffer_search};
20use settings::{Settings, SettingsStore};
21use ui::{
22    ButtonStyle, ContextMenu, ContextMenuEntry, DocumentationSide, IconButton, IconName, IconSize,
23    PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*,
24};
25use vim_mode_setting::{HelixModeSetting, VimModeSetting};
26use workspace::item::ItemBufferKind;
27use workspace::{
28    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::ItemHandle,
29};
30use zed_actions::{agent::AddSelectionToThread, assistant::InlineAssist, outline::ToggleOutline};
31
32const MAX_CODE_ACTION_MENU_LINES: u32 = 16;
33
34pub struct QuickActionBar {
35    _inlay_hints_enabled_subscription: Option<Subscription>,
36    _ai_settings_subscription: Subscription,
37    active_item: Option<Box<dyn ItemHandle>>,
38    buffer_search_bar: Entity<BufferSearchBar>,
39    show: bool,
40    toggle_selections_handle: PopoverMenuHandle<ContextMenu>,
41    toggle_settings_handle: PopoverMenuHandle<ContextMenu>,
42    workspace: WeakEntity<Workspace>,
43}
44
45impl QuickActionBar {
46    pub fn new(
47        buffer_search_bar: Entity<BufferSearchBar>,
48        workspace: &Workspace,
49        cx: &mut Context<Self>,
50    ) -> Self {
51        let mut was_agent_enabled = AgentSettings::get_global(cx).enabled(cx);
52        let mut was_agent_button = AgentSettings::get_global(cx).button;
53
54        let ai_settings_subscription = cx.observe_global::<SettingsStore>(move |_, cx| {
55            let agent_settings = AgentSettings::get_global(cx);
56            let is_agent_enabled = agent_settings.enabled(cx);
57
58            if was_agent_enabled != is_agent_enabled || was_agent_button != agent_settings.button {
59                was_agent_enabled = is_agent_enabled;
60                was_agent_button = agent_settings.button;
61                cx.notify();
62            }
63        });
64
65        let mut this = Self {
66            _inlay_hints_enabled_subscription: None,
67            _ai_settings_subscription: ai_settings_subscription,
68            active_item: None,
69            buffer_search_bar,
70            show: true,
71            toggle_selections_handle: Default::default(),
72            toggle_settings_handle: Default::default(),
73            workspace: workspace.weak_handle(),
74        };
75        this.apply_settings(cx);
76        cx.observe_global::<SettingsStore>(|this, cx| this.apply_settings(cx))
77            .detach();
78        this
79    }
80
81    fn active_editor(&self) -> Option<Entity<Editor>> {
82        self.active_item
83            .as_ref()
84            .and_then(|item| item.downcast::<Editor>())
85    }
86
87    fn apply_settings(&mut self, cx: &mut Context<Self>) {
88        let new_show = EditorSettings::get_global(cx).toolbar.quick_actions;
89        if new_show != self.show {
90            self.show = new_show;
91            cx.emit(ToolbarItemEvent::ChangeLocation(
92                self.get_toolbar_item_location(),
93            ));
94        }
95    }
96
97    fn get_toolbar_item_location(&self) -> ToolbarItemLocation {
98        if self.show && self.active_editor().is_some() {
99            ToolbarItemLocation::PrimaryRight
100        } else {
101            ToolbarItemLocation::Hidden
102        }
103    }
104}
105
106impl Render for QuickActionBar {
107    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
108        let Some(editor) = self.active_editor() else {
109            return div().id("empty quick action bar");
110        };
111
112        let supports_inlay_hints = editor.update(cx, |editor, cx| editor.supports_inlay_hints(cx));
113        let supports_semantic_tokens =
114            editor.update(cx, |editor, cx| editor.supports_semantic_tokens(cx));
115        let supports_code_lens = editor.update(cx, |editor, cx| editor.supports_code_lens(cx));
116        let editor_value = editor.read(cx);
117        let selection_menu_enabled = editor_value.selection_menu_enabled(cx);
118        let inlay_hints_enabled = editor_value.inlay_hints_enabled();
119        let inline_values_enabled = editor_value.inline_values_enabled();
120        let semantic_highlights_enabled = editor_value.semantic_highlights_enabled();
121        let code_lens_enabled = editor_value.code_lens_enabled();
122        let is_full = editor_value.mode().is_full();
123        let diagnostics_enabled = editor_value.diagnostics_enabled()
124            && editor_value.diagnostics_max_severity != DiagnosticSeverity::Off;
125        let supports_inline_diagnostics = editor_value.inline_diagnostics_enabled();
126        let inline_diagnostics_enabled = editor_value.show_inline_diagnostics();
127        let git_blame_inline_enabled = editor_value.git_blame_inline_enabled();
128        let show_git_blame_gutter = editor_value.show_git_blame_gutter();
129        let auto_signature_help_enabled = editor_value.auto_signature_help_enabled(cx);
130        let show_line_numbers = editor_value.line_numbers_enabled(cx);
131        let has_edit_prediction_provider = editor_value.edit_prediction_provider().is_some();
132        let show_edit_predictions = editor_value.edit_predictions_enabled();
133        let edit_predictions_enabled_at_cursor =
134            editor_value.edit_predictions_enabled_at_cursor(cx);
135        let supports_minimap = editor_value.supports_minimap(cx);
136        let minimap_enabled = supports_minimap && editor_value.minimap().is_some();
137        let has_available_code_actions = editor_value.has_available_code_actions_for_selection();
138        let code_action_enabled = editor_value.code_actions_enabled_for_toolbar(cx);
139        let focus_handle = editor_value.focus_handle(cx);
140
141        let search_button = (editor.buffer_kind(cx) == ItemBufferKind::Singleton).then(|| {
142            QuickActionBarButton::new(
143                "toggle buffer search",
144                search::SEARCH_ICON,
145                !self.buffer_search_bar.read(cx).is_dismissed(),
146                Box::new(buffer_search::Deploy::find()),
147                focus_handle.clone(),
148                "Buffer Search",
149                {
150                    let buffer_search_bar = self.buffer_search_bar.clone();
151                    move |_, window, cx| {
152                        buffer_search_bar.update(cx, |search_bar, cx| {
153                            search_bar.toggle(&buffer_search::Deploy::find(), window, cx)
154                        });
155                    }
156                },
157            )
158        });
159
160        let assistant_button = QuickActionBarButton::new(
161            "toggle inline assistant",
162            IconName::OmegaAssistant,
163            false,
164            Box::new(InlineAssist::default()),
165            focus_handle,
166            "Inline Assist",
167            move |_, window, cx| {
168                window.dispatch_action(Box::new(InlineAssist::default()), cx);
169            },
170        );
171
172        let code_actions_dropdown = code_action_enabled.then(|| {
173            let is_deployed = {
174                let menu_ref = editor.read(cx).context_menu().borrow();
175                let code_action_menu = menu_ref
176                    .as_ref()
177                    .filter(|menu| matches!(menu, CodeContextMenu::CodeActions(..)));
178                code_action_menu
179                    .as_ref()
180                    .is_some_and(|menu| matches!(menu.origin(), ContextMenuOrigin::QuickActionBar))
181            };
182            let code_action_element = is_deployed
183                .then(|| {
184                    editor.update(cx, |editor, cx| {
185                        editor.render_context_menu(MAX_CODE_ACTION_MENU_LINES, window, cx)
186                    })
187                })
188                .flatten();
189            v_flex()
190                .child(
191                    IconButton::new("toggle_code_actions_icon", IconName::BoltOutlined)
192                        .icon_size(IconSize::Small)
193                        .style(ButtonStyle::Subtle)
194                        .disabled(!has_available_code_actions)
195                        .toggle_state(is_deployed)
196                        .when(!is_deployed, |this| {
197                            this.when(has_available_code_actions, |this| {
198                                this.tooltip(Tooltip::for_action_title(
199                                    "Code Actions",
200                                    &ToggleCodeActions::default(),
201                                ))
202                            })
203                            .when(
204                                !has_available_code_actions,
205                                |this| {
206                                    this.tooltip(Tooltip::for_action_title(
207                                        "No Code Actions Available",
208                                        &ToggleCodeActions::default(),
209                                    ))
210                                },
211                            )
212                        })
213                        .on_click({
214                            let editor = editor.clone();
215                            move |_, window, cx| {
216                                editor.update(cx, |editor, cx| {
217                                    editor.toggle_code_actions(
218                                        &ToggleCodeActions {
219                                            deployed_from: Some(CodeActionSource::QuickActionBar),
220                                            quick_launch: false,
221                                        },
222                                        window,
223                                        cx,
224                                    );
225                                })
226                            }
227                        }),
228                )
229                .children(code_action_element.map(|menu| {
230                    deferred(
231                        anchored()
232                            .position_mode(AnchoredPositionMode::Local)
233                            .position(point(px(20.), px(20.)))
234                            .anchor(Anchor::TopRight)
235                            .child(menu),
236                    )
237                }))
238        });
239
240        let editor_selections_dropdown = selection_menu_enabled.then(|| {
241            let has_diff_hunks = editor
242                .read(cx)
243                .buffer()
244                .read(cx)
245                .snapshot(cx)
246                .has_diff_hunks();
247            let has_selection = editor.update(cx, |editor, cx| {
248                editor.has_non_empty_selection(&editor.display_snapshot(cx))
249            });
250
251            let focus = editor.focus_handle(cx);
252
253            let disable_ai = DisableAiSettings::get_global(cx).disable_ai;
254
255            PopoverMenu::new("editor-selections-dropdown")
256                .trigger_with_tooltip(
257                    IconButton::new("toggle_editor_selections_icon", IconName::CursorIBeam)
258                        .icon_size(IconSize::Small)
259                        .style(ButtonStyle::Subtle)
260                        .toggle_state(self.toggle_selections_handle.is_deployed()),
261                    Tooltip::text("Selection Controls"),
262                )
263                .with_handle(self.toggle_selections_handle.clone())
264                .anchor(Anchor::TopRight)
265                .menu(move |window, cx| {
266                    let focus = focus.clone();
267                    let menu = ContextMenu::build(window, cx, move |menu, _, _| {
268                        menu.context(focus.clone())
269                            .action("Select All", Box::new(SelectAll))
270                            .action(
271                                "Select Next Occurrence",
272                                Box::new(SelectNext {
273                                    replace_newest: false,
274                                }),
275                            )
276                            .action("Expand Selection", Box::new(SelectLargerSyntaxNode))
277                            .action("Shrink Selection", Box::new(SelectSmallerSyntaxNode))
278                            .action(
279                                "Add Cursor Above",
280                                Box::new(AddSelectionAbove {
281                                    skip_soft_wrap: true,
282                                }),
283                            )
284                            .action(
285                                "Add Cursor Below",
286                                Box::new(AddSelectionBelow {
287                                    skip_soft_wrap: true,
288                                }),
289                            )
290                            .when(!disable_ai, |this| {
291                                this.separator().action_disabled_when(
292                                    !has_selection,
293                                    "Add to Agent Thread",
294                                    Box::new(AddSelectionToThread),
295                                )
296                            })
297                            .separator()
298                            .action("Go to Symbol", Box::new(ToggleOutline))
299                            .action("Go to Line/Column", Box::new(ToggleGoToLine))
300                            .separator()
301                            .action("Next Problem", Box::new(GoToDiagnostic::default()))
302                            .action(
303                                "Previous Problem",
304                                Box::new(GoToPreviousDiagnostic::default()),
305                            )
306                            .separator()
307                            .action_disabled_when(!has_diff_hunks, "Next Hunk", Box::new(GoToHunk))
308                            .action_disabled_when(
309                                !has_diff_hunks,
310                                "Previous Hunk",
311                                Box::new(GoToPreviousHunk),
312                            )
313                            .separator()
314                            .action("Move Line Up", Box::new(MoveLineUp))
315                            .action("Move Line Down", Box::new(MoveLineDown))
316                            .action("Duplicate Selection", Box::new(DuplicateLineDown))
317                    });
318                    Some(menu)
319                })
320        });
321
322        let editor_focus_handle = editor.focus_handle(cx);
323        let editor = editor.downgrade();
324        let editor_settings_dropdown = {
325            let vim_mode_enabled = VimModeSetting::get_global(cx).0;
326            let helix_mode_enabled = HelixModeSetting::get_global(cx).0;
327
328            PopoverMenu::new("editor-settings")
329                .trigger_with_tooltip(
330                    IconButton::new("toggle_editor_settings_icon", IconName::Filter)
331                        .icon_size(IconSize::Small)
332                        .toggle_state(self.toggle_settings_handle.is_deployed()),
333                    Tooltip::text("Editor Controls"),
334                )
335                .anchor(Anchor::TopRight)
336                .with_handle(self.toggle_settings_handle.clone())
337                .menu(move |window, cx| {
338                    let menu = ContextMenu::build(window, cx, {
339                        let focus_handle = editor_focus_handle.clone();
340                        |mut menu, _, _| {
341                            menu = menu.context(focus_handle);
342
343                            if supports_inlay_hints {
344                                menu = menu.toggleable_entry(
345                                    "Inlay Hints",
346                                    inlay_hints_enabled,
347                                    IconPosition::Start,
348                                    Some(editor::actions::ToggleInlayHints.boxed_clone()),
349                                    {
350                                        let editor = editor.clone();
351                                        move |window, cx| {
352                                            editor
353                                                .update(cx, |editor, cx| {
354                                                    editor.toggle_inlay_hints(
355                                                        &editor::actions::ToggleInlayHints,
356                                                        window,
357                                                        cx,
358                                                    );
359                                                })
360                                                .ok();
361                                        }
362                                    },
363                                );
364
365                                menu = menu.toggleable_entry(
366                                    "Inline Values",
367                                    inline_values_enabled,
368                                    IconPosition::Start,
369                                    Some(editor::actions::ToggleInlineValues.boxed_clone()),
370                                    {
371                                        let editor = editor.clone();
372                                        move |window, cx| {
373                                            editor
374                                                .update(cx, |editor, cx| {
375                                                    editor.toggle_inline_values(
376                                                        &editor::actions::ToggleInlineValues,
377                                                        window,
378                                                        cx,
379                                                    );
380                                                })
381                                                .ok();
382                                        }
383                                    }
384                                );
385                            }
386
387                            if supports_semantic_tokens {
388                                menu = menu.toggleable_entry(
389                                    "Semantic Highlights",
390                                    semantic_highlights_enabled,
391                                    IconPosition::Start,
392                                    Some(editor::actions::ToggleSemanticHighlights.boxed_clone()),
393                                    {
394                                        let editor = editor.clone();
395                                        move |window, cx| {
396                                            editor
397                                                .update(cx, |editor, cx| {
398                                                    editor.toggle_semantic_highlights(
399                                                        &editor::actions::ToggleSemanticHighlights,
400                                                        window,
401                                                        cx,
402                                                    );
403                                                })
404                                                .ok();
405                                        }
406                                    },
407                                );
408                            }
409
410                            if supports_code_lens {
411                                menu = menu.toggleable_entry(
412                                    "Code Lens",
413                                    code_lens_enabled,
414                                    IconPosition::Start,
415                                    Some(editor::actions::ToggleCodeLens.boxed_clone()),
416                                    {
417                                        let editor = editor.clone();
418                                        move |window, cx| {
419                                            editor
420                                                .update(cx, |editor, cx| {
421                                                    editor.toggle_code_lens_action(
422                                                        &editor::actions::ToggleCodeLens,
423                                                        window,
424                                                        cx,
425                                                    );
426                                                })
427                                                .ok();
428                                        }
429                                    },
430                                );
431                            }
432
433                            if supports_minimap {
434                                menu = menu.toggleable_entry("Minimap", minimap_enabled, IconPosition::Start, Some(editor::actions::ToggleMinimap.boxed_clone()), {
435                                    let editor = editor.clone();
436                                    move |window, cx| {
437                                        editor
438                                            .update(cx, |editor, cx| {
439                                                editor.toggle_minimap(
440                                                    &editor::actions::ToggleMinimap,
441                                                    window,
442                                                    cx,
443                                                );
444                                            })
445                                            .ok();
446                                    }
447                                },)
448                            }
449
450                            if has_edit_prediction_provider {
451                                let mut edit_prediction_entry = ContextMenuEntry::new("Edit Predictions")
452                                    .toggleable(IconPosition::Start, edit_predictions_enabled_at_cursor && show_edit_predictions)
453                                    .disabled(!edit_predictions_enabled_at_cursor)
454                                    .action(
455                                        editor::actions::ToggleEditPrediction.boxed_clone(),
456                                    ).handler({
457                                        let editor = editor.clone();
458                                        move |window, cx| {
459                                            editor
460                                                .update(cx, |editor, cx| {
461                                                    editor.toggle_edit_predictions(
462                                                        &editor::actions::ToggleEditPrediction,
463                                                        window,
464                                                        cx,
465                                                    );
466                                                })
467                                                .ok();
468                                        }
469                                    });
470                                if !edit_predictions_enabled_at_cursor {
471                                    edit_prediction_entry = edit_prediction_entry.documentation_aside(DocumentationSide::Left, |_| {
472                                        Label::new("You can't toggle edit predictions for this file as it is within the excluded files list.").into_any_element()
473                                    });
474                                }
475
476                                menu = menu.item(edit_prediction_entry);
477                            }
478
479                            menu = menu.separator();
480
481                            if is_full {
482                                menu = menu.toggleable_entry(
483                                    "Diagnostics",
484                                    diagnostics_enabled,
485                                    IconPosition::Start,
486                                    Some(ToggleDiagnostics.boxed_clone()),
487                                    {
488                                        let editor = editor.clone();
489                                        move |window, cx| {
490                                            editor
491                                                .update(cx, |editor, cx| {
492                                                    editor.toggle_diagnostics(
493                                                        &ToggleDiagnostics,
494                                                        window,
495                                                        cx,
496                                                    );
497                                                })
498                                                .ok();
499                                        }
500                                    },
501                                );
502
503                                if supports_inline_diagnostics {
504                                    let mut inline_diagnostics_item = ContextMenuEntry::new("Inline Diagnostics")
505                                        .toggleable(IconPosition::Start, diagnostics_enabled && inline_diagnostics_enabled)
506                                        .action(ToggleInlineDiagnostics.boxed_clone())
507                                        .handler({
508                                            let editor = editor.clone();
509                                            move |window, cx| {
510                                                editor
511                                                    .update(cx, |editor, cx| {
512                                                        editor.toggle_inline_diagnostics(
513                                                            &ToggleInlineDiagnostics,
514                                                            window,
515                                                            cx,
516                                                        );
517                                                    })
518                                                    .ok();
519                                            }
520                                        });
521                                    if !diagnostics_enabled {
522                                        inline_diagnostics_item = inline_diagnostics_item.disabled(true).documentation_aside(DocumentationSide::Left, |_|  Label::new("Inline diagnostics are not available until regular diagnostics are enabled.").into_any_element());
523                                    }
524                                    menu = menu.item(inline_diagnostics_item)
525                                }
526
527                                menu = menu.separator();
528                            }
529
530                            menu = menu.toggleable_entry(
531                                "Line Numbers",
532                                show_line_numbers,
533                                IconPosition::Start,
534                                Some(editor::actions::ToggleLineNumbers.boxed_clone()),
535                                {
536                                    let editor = editor.clone();
537                                    move |window, cx| {
538                                        editor
539                                            .update(cx, |editor, cx| {
540                                                editor.toggle_line_numbers(
541                                                    &editor::actions::ToggleLineNumbers,
542                                                    window,
543                                                    cx,
544                                                );
545                                            })
546                                            .ok();
547                                    }
548                                },
549                            );
550
551                            menu = menu.toggleable_entry(
552                                "Selection Menu",
553                                selection_menu_enabled,
554                                IconPosition::Start,
555                                Some(editor::actions::ToggleSelectionMenu.boxed_clone()),
556                                {
557                                    let editor = editor.clone();
558                                    move |window, cx| {
559                                        editor
560                                            .update(cx, |editor, cx| {
561                                                editor.toggle_selection_menu(
562                                                    &editor::actions::ToggleSelectionMenu,
563                                                    window,
564                                                    cx,
565                                                )
566                                            })
567                                            .ok();
568                                    }
569                                },
570                            );
571
572                            menu = menu.toggleable_entry(
573                                "Auto Signature Help",
574                                auto_signature_help_enabled,
575                                IconPosition::Start,
576                                Some(editor::actions::ToggleAutoSignatureHelp.boxed_clone()),
577                                {
578                                    let editor = editor.clone();
579                                    move |window, cx| {
580                                        editor
581                                            .update(cx, |editor, cx| {
582                                                editor.toggle_auto_signature_help_menu(
583                                                    &editor::actions::ToggleAutoSignatureHelp,
584                                                    window,
585                                                    cx,
586                                                );
587                                            })
588                                            .ok();
589                                    }
590                                },
591                            );
592
593                            menu = menu.separator();
594
595                            menu = menu.toggleable_entry(
596                                "Inline Git Blame",
597                                git_blame_inline_enabled,
598                                IconPosition::Start,
599                                Some(editor::actions::ToggleGitBlameInline.boxed_clone()),
600                                {
601                                    let editor = editor.clone();
602                                    move |window, cx| {
603                                        editor
604                                            .update(cx, |editor, cx| {
605                                                editor.toggle_git_blame_inline(
606                                                    &editor::actions::ToggleGitBlameInline,
607                                                    window,
608                                                    cx,
609                                                )
610                                            })
611                                            .ok();
612                                    }
613                                },
614                            );
615
616                            menu = menu.toggleable_entry(
617                                "Column Git Blame",
618                                show_git_blame_gutter,
619                                IconPosition::Start,
620                                Some(git::Blame.boxed_clone()),
621                                {
622                                    let editor = editor.clone();
623                                    move |window, cx| {
624                                        editor
625                                            .update(cx, |editor, cx| {
626                                                editor.toggle_git_blame(
627                                                    &git::Blame,
628                                                    window,
629                                                    cx,
630                                                )
631                                            })
632                                            .ok();
633                                    }
634                                },
635                            );
636
637                            menu = menu.separator();
638
639                            menu = menu.toggleable_entry(
640                                "Vim Mode",
641                                vim_mode_enabled,
642                                IconPosition::Start,
643                                None,
644                                {
645                                    move |window, cx| {
646                                        let new_value = !vim_mode_enabled;
647                                        VimModeSetting::override_global(VimModeSetting(new_value), cx);
648                                        HelixModeSetting::override_global(HelixModeSetting(false), cx);
649                                        window.refresh();
650                                    }
651                                },
652                            );
653                            menu = menu.toggleable_entry(
654                                "Helix Mode",
655                                helix_mode_enabled,
656                                IconPosition::Start,
657                                None,
658                                {
659                                    move |window, cx| {
660                                        let new_value = !helix_mode_enabled;
661                                        HelixModeSetting::override_global(HelixModeSetting(new_value), cx);
662                                        VimModeSetting::override_global(VimModeSetting(false), cx);
663                                        window.refresh();
664                                    }
665                                }
666                            );
667
668                            menu
669                        }
670                    });
671                    Some(menu)
672                })
673        };
674
675        h_flex()
676            .id("quick action bar")
677            .gap(DynamicSpacing::Base01.rems(cx))
678            .children(self.render_repl_menu(cx))
679            .children(self.render_preview_button(cx))
680            .children(search_button)
681            .when(
682                AgentSettings::get_global(cx).enabled(cx) && AgentSettings::get_global(cx).button,
683                |bar| bar.child(assistant_button),
684            )
685            .children(code_actions_dropdown)
686            .children(editor_selections_dropdown)
687            .child(editor_settings_dropdown)
688    }
689}
690
691impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}
692
693#[derive(IntoElement)]
694struct QuickActionBarButton {
695    id: ElementId,
696    icon: IconName,
697    toggled: bool,
698    action: Box<dyn Action>,
699    focus_handle: FocusHandle,
700    tooltip: SharedString,
701    on_click: Box<dyn Fn(&ClickEvent, &mut Window, &mut App)>,
702}
703
704impl QuickActionBarButton {
705    fn new(
706        id: impl Into<ElementId>,
707        icon: IconName,
708        toggled: bool,
709        action: Box<dyn Action>,
710        focus_handle: FocusHandle,
711        tooltip: impl Into<SharedString>,
712        on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
713    ) -> Self {
714        Self {
715            id: id.into(),
716            icon,
717            toggled,
718            action,
719            focus_handle,
720            tooltip: tooltip.into(),
721            on_click: Box::new(on_click),
722        }
723    }
724}
725
726impl RenderOnce for QuickActionBarButton {
727    fn render(self, _window: &mut Window, _: &mut App) -> impl IntoElement {
728        let tooltip = self.tooltip.clone();
729        let action = self.action.boxed_clone();
730
731        IconButton::new(self.id.clone(), self.icon)
732            .icon_size(IconSize::Small)
733            .style(ButtonStyle::Subtle)
734            .toggle_state(self.toggled)
735            .tooltip(move |_window, cx| {
736                Tooltip::for_action_in(tooltip.clone(), &*action, &self.focus_handle, cx)
737            })
738            .on_click(move |event, window, cx| (self.on_click)(event, window, cx))
739    }
740}
741
742impl ToolbarItemView for QuickActionBar {
743    fn set_active_pane_item(
744        &mut self,
745        active_pane_item: Option<&dyn ItemHandle>,
746        _: &mut Window,
747        cx: &mut Context<Self>,
748    ) -> ToolbarItemLocation {
749        self.active_item = active_pane_item.map(ItemHandle::boxed_clone);
750        if let Some(active_item) = active_pane_item {
751            self._inlay_hints_enabled_subscription.take();
752
753            if let Some(editor) = active_item.downcast::<Editor>() {
754                let (
755                    mut inlay_hints_enabled,
756                    mut supports_inlay_hints,
757                    mut supports_semantic_tokens,
758                ) = editor.update(cx, |editor, cx| {
759                    (
760                        editor.inlay_hints_enabled(),
761                        editor.supports_inlay_hints(cx),
762                        editor.supports_semantic_tokens(cx),
763                    )
764                });
765                self._inlay_hints_enabled_subscription =
766                    Some(cx.observe(&editor, move |_, editor, cx| {
767                        let (
768                            new_inlay_hints_enabled,
769                            new_supports_inlay_hints,
770                            new_supports_semantic_tokens,
771                        ) = editor.update(cx, |editor, cx| {
772                            (
773                                editor.inlay_hints_enabled(),
774                                editor.supports_inlay_hints(cx),
775                                editor.supports_semantic_tokens(cx),
776                            )
777                        });
778                        let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
779                            || supports_inlay_hints != new_supports_inlay_hints
780                            || supports_semantic_tokens != new_supports_semantic_tokens;
781                        inlay_hints_enabled = new_inlay_hints_enabled;
782                        supports_inlay_hints = new_supports_inlay_hints;
783                        supports_semantic_tokens = new_supports_semantic_tokens;
784                        if should_notify {
785                            cx.notify()
786                        }
787                    }));
788            }
789        }
790        self.get_toolbar_item_location()
791    }
792}
793
Served at tenant.openagents/omega Member data and write actions are omitted.