agent_ui: Fix keybinding display inside tooltips in the footer actions (#48909)

Danilo Leal created

- [x] Code Reviewed
- [x] Manual QA

Release Notes:

- Agent: Fixed an issue where keybindings inside tooltips in the message
editor footer actions weren't being displayed.

Change summary

assets/keymaps/default-linux.json                   |  1 
assets/keymaps/default-macos.json                   |  1 
assets/keymaps/default-windows.json                 |  1 
crates/agent_ui/src/acp/mode_selector.rs            | 24 +++-----------
crates/agent_ui/src/acp/model_selector_popover.rs   | 16 +--------
crates/agent_ui/src/acp/thread_view.rs              |  5 --
crates/agent_ui/src/agent_model_selector.rs         | 10 +-----
crates/agent_ui/src/profile_selector.rs             | 15 +++-----
crates/agent_ui/src/text_thread_editor.rs           |  4 -
crates/agent_ui/src/ui/model_selector_components.rs | 16 +--------
10 files changed, 24 insertions(+), 69 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -247,6 +247,7 @@
       "ctrl-alt-p": "agent::ManageProfiles",
       "ctrl-alt-l": "agent::OpenRulesLibrary",
       "ctrl-i": "agent::ToggleProfileSelector",
+      "shift-tab": "agent::CycleModeSelector",
       "ctrl-alt-/": "agent::ToggleModelSelector",
       "alt-tab": "agent::CycleFavoriteModels",
       // `alt-l` is provided as an alternative to `alt-tab` as the latter breaks on Linux under the `AgentPanel` context

assets/keymaps/default-macos.json 🔗

@@ -288,6 +288,7 @@
       "cmd-alt-l": "agent::OpenRulesLibrary",
       "cmd-alt-p": "agent::ManageProfiles",
       "cmd-i": "agent::ToggleProfileSelector",
+      "shift-tab": "agent::CycleModeSelector",
       "cmd-alt-/": "agent::ToggleModelSelector",
       "alt-tab": "agent::CycleFavoriteModels",
       "cmd-shift-j": "agent::ToggleNavigationMenu",

assets/keymaps/default-windows.json 🔗

@@ -248,6 +248,7 @@
       "shift-alt-l": "agent::OpenRulesLibrary",
       "shift-alt-p": "agent::ManageProfiles",
       "ctrl-i": "agent::ToggleProfileSelector",
+      "shift-tab": "agent::CycleModeSelector",
       "alt-tab": "agent::CycleFavoriteModels",
       // `alt-l` is provided as an alternative to `alt-tab` as the latter breaks on Windows under the `AgentPanel` context
       "alt-l": "agent::CycleFavoriteModels",

crates/agent_ui/src/acp/mode_selector.rs 🔗

@@ -3,7 +3,7 @@ use agent_client_protocol as acp;
 use agent_servers::AgentServer;
 use agent_settings::AgentSettings;
 use fs::Fs;
-use gpui::{Context, Entity, FocusHandle, WeakEntity, Window, prelude::*};
+use gpui::{Context, Entity, WeakEntity, Window, prelude::*};
 use settings::Settings as _;
 use std::{rc::Rc, sync::Arc};
 use ui::{
@@ -17,7 +17,6 @@ pub struct ModeSelector {
     connection: Rc<dyn AgentSessionModes>,
     agent_server: Rc<dyn AgentServer>,
     menu_handle: PopoverMenuHandle<ContextMenu>,
-    focus_handle: FocusHandle,
     fs: Arc<dyn Fs>,
     setting_mode: bool,
 }
@@ -27,7 +26,6 @@ impl ModeSelector {
         session_modes: Rc<dyn AgentSessionModes>,
         agent_server: Rc<dyn AgentServer>,
         fs: Arc<dyn Fs>,
-        focus_handle: FocusHandle,
     ) -> Self {
         Self {
             connection: session_modes,
@@ -35,7 +33,6 @@ impl ModeSelector {
             menu_handle: PopoverMenuHandle::default(),
             fs,
             setting_mode: false,
-            focus_handle,
         }
     }
 
@@ -182,7 +179,6 @@ impl Render for ModeSelector {
             .trigger_with_tooltip(
                 trigger_button,
                 Tooltip::element({
-                    let focus_handle = self.focus_handle.clone();
                     move |_window, cx| {
                         v_flex()
                             .gap_1()
@@ -191,25 +187,17 @@ impl Render for ModeSelector {
                                     .gap_2()
                                     .justify_between()
                                     .child(Label::new("Change Mode"))
-                                    .child(KeyBinding::for_action_in(
-                                        &ToggleProfileSelector,
-                                        &focus_handle,
-                                        cx,
-                                    )),
+                                    .child(KeyBinding::for_action(&ToggleProfileSelector, cx)),
                             )
                             .child(
                                 h_flex()
-                                    .pb_1()
+                                    .pt_1()
                                     .gap_2()
-                                    .justify_between()
-                                    .border_b_1()
+                                    .border_t_1()
                                     .border_color(cx.theme().colors().border_variant)
+                                    .justify_between()
                                     .child(Label::new("Cycle Through Modes"))
-                                    .child(KeyBinding::for_action_in(
-                                        &CycleModeSelector,
-                                        &focus_handle,
-                                        cx,
-                                    )),
+                                    .child(KeyBinding::for_action(&CycleModeSelector, cx)),
                             )
                             .into_any()
                     }

crates/agent_ui/src/acp/model_selector_popover.rs 🔗

@@ -13,7 +13,6 @@ use crate::ui::ModelSelectorTooltip;
 pub struct AcpModelSelectorPopover {
     selector: Entity<AcpModelSelector>,
     menu_handle: PopoverMenuHandle<AcpModelSelector>,
-    focus_handle: FocusHandle,
 }
 
 impl AcpModelSelectorPopover {
@@ -26,20 +25,11 @@ impl AcpModelSelectorPopover {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Self {
-        let focus_handle_clone = focus_handle.clone();
         Self {
             selector: cx.new(move |cx| {
-                acp_model_selector(
-                    selector,
-                    agent_server,
-                    fs,
-                    focus_handle_clone.clone(),
-                    window,
-                    cx,
-                )
+                acp_model_selector(selector, agent_server, fs, focus_handle.clone(), window, cx)
             }),
             menu_handle,
-            focus_handle,
         }
     }
 
@@ -69,8 +59,6 @@ impl Render for AcpModelSelectorPopover {
 
         let model_icon = model.as_ref().and_then(|model| model.icon.clone());
 
-        let focus_handle = self.focus_handle.clone();
-
         let (color, icon) = if self.menu_handle.is_deployed() {
             (Color::Accent, IconName::ChevronUp)
         } else {
@@ -81,7 +69,7 @@ impl Render for AcpModelSelectorPopover {
 
         let tooltip = Tooltip::element({
             move |_, _cx| {
-                ModelSelectorTooltip::new(focus_handle.clone())
+                ModelSelectorTooltip::new()
                     .show_cycle_row(show_cycle_row)
                     .into_any_element()
             }

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -707,10 +707,7 @@ impl AcpServerView {
                 .session_modes(&session_id, cx)
                 .map(|session_modes| {
                     let fs = self.project.read(cx).fs().clone();
-                    let focus_handle = self.focus_handle(cx);
-                    cx.new(|_cx| {
-                        ModeSelector::new(session_modes, self.agent.clone(), fs, focus_handle)
-                    })
+                    cx.new(|_cx| ModeSelector::new(session_modes, self.agent.clone(), fs))
                 });
         }
 

crates/agent_ui/src/agent_model_selector.rs 🔗

@@ -14,7 +14,6 @@ use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*};
 pub struct AgentModelSelector {
     selector: Entity<LanguageModelSelector>,
     menu_handle: PopoverMenuHandle<LanguageModelSelector>,
-    focus_handle: FocusHandle,
 }
 
 impl AgentModelSelector {
@@ -26,8 +25,6 @@ impl AgentModelSelector {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Self {
-        let focus_handle_clone = focus_handle.clone();
-
         Self {
             selector: cx.new(move |cx| {
                 language_model_selector(
@@ -64,13 +61,12 @@ impl AgentModelSelector {
                         }
                     },
                     true, // Use popover styles for picker
-                    focus_handle_clone,
+                    focus_handle.clone(),
                     window,
                     cx,
                 )
             }),
             menu_handle,
-            focus_handle,
         }
     }
 
@@ -106,11 +102,9 @@ impl Render for AgentModelSelector {
 
         let show_cycle_row = self.selector.read(cx).delegate.favorites_count() > 1;
 
-        let focus_handle = self.focus_handle.clone();
-
         let tooltip = Tooltip::element({
             move |_, _cx| {
-                ModelSelectorTooltip::new(focus_handle.clone())
+                ModelSelectorTooltip::new()
                     .show_cycle_row(show_cycle_row)
                     .into_any_element()
             }

crates/agent_ui/src/profile_selector.rs 🔗

@@ -167,7 +167,6 @@ impl Render for ProfileSelector {
         let selected_profile = profile
             .map(|profile| profile.name.clone())
             .unwrap_or_else(|| "Unknown".into());
-        let focus_handle = self.focus_handle.clone();
 
         let icon = if self.picker_handle.is_deployed() {
             IconName::ChevronUp
@@ -192,20 +191,18 @@ impl Render for ProfileSelector {
                     let container = || h_flex().gap_1().justify_between();
                     v_flex()
                         .gap_1()
-                        .child(container().child(Label::new("Change Profile")).child(
-                            KeyBinding::for_action_in(&ToggleProfileSelector, &focus_handle, cx),
-                        ))
+                        .child(
+                            container()
+                                .child(Label::new("Change Profile"))
+                                .child(KeyBinding::for_action(&ToggleProfileSelector, cx)),
+                        )
                         .child(
                             container()
                                 .pt_1()
                                 .border_t_1()
                                 .border_color(cx.theme().colors().border_variant)
                                 .child(Label::new("Cycle Through Profiles"))
-                                .child(KeyBinding::for_action_in(
-                                    &CycleModeSelector,
-                                    &focus_handle,
-                                    cx,
-                                )),
+                                .child(KeyBinding::for_action(&CycleModeSelector, cx)),
                         )
                         .into_any()
                 }

crates/agent_ui/src/text_thread_editor.rs 🔗

@@ -2252,8 +2252,6 @@ impl TextThreadEditor {
             .map(|p| p.icon())
             .unwrap_or(IconOrSvg::Icon(IconName::Ai));
 
-        let focus_handle = self.editor().focus_handle(cx);
-
         let (color, icon) = if self.language_model_selector_menu_handle.is_deployed() {
             (Color::Accent, IconName::ChevronUp)
         } else {
@@ -2276,7 +2274,7 @@ impl TextThreadEditor {
 
         let tooltip = Tooltip::element({
             move |_, _cx| {
-                ModelSelectorTooltip::new(focus_handle.clone())
+                ModelSelectorTooltip::new()
                     .show_cycle_row(show_cycle_row)
                     .into_any_element()
             }

crates/agent_ui/src/ui/model_selector_components.rs 🔗

@@ -204,14 +204,12 @@ impl RenderOnce for ModelSelectorFooter {
 
 #[derive(IntoElement)]
 pub struct ModelSelectorTooltip {
-    focus_handle: FocusHandle,
     show_cycle_row: bool,
 }
 
 impl ModelSelectorTooltip {
-    pub fn new(focus_handle: FocusHandle) -> Self {
+    pub fn new() -> Self {
         Self {
-            focus_handle,
             show_cycle_row: true,
         }
     }
@@ -231,11 +229,7 @@ impl RenderOnce for ModelSelectorTooltip {
                     .gap_2()
                     .justify_between()
                     .child(Label::new("Change Model"))
-                    .child(KeyBinding::for_action_in(
-                        &ToggleModelSelector,
-                        &self.focus_handle,
-                        cx,
-                    )),
+                    .child(KeyBinding::for_action(&ToggleModelSelector, cx)),
             )
             .when(self.show_cycle_row, |this| {
                 this.child(
@@ -246,11 +240,7 @@ impl RenderOnce for ModelSelectorTooltip {
                         .border_color(cx.theme().colors().border_variant)
                         .justify_between()
                         .child(Label::new("Cycle Favorited Models"))
-                        .child(KeyBinding::for_action_in(
-                            &CycleFavoriteModels,
-                            &self.focus_handle,
-                            cx,
-                        )),
+                        .child(KeyBinding::for_action(&CycleFavoriteModels, cx)),
                 )
             })
     }