`impl Focusable for Button`

Lukas Wirth created

Change summary

crates/agent_ui/src/acp/thread_view.rs                                    | 20 
crates/agent_ui/src/active_thread.rs                                      | 10 
crates/agent_ui/src/agent_configuration.rs                                |  6 
crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs         |  8 
crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs |  4 
crates/agent_ui/src/agent_diff.rs                                         | 14 
crates/agent_ui/src/agent_panel.rs                                        | 54 
crates/agent_ui/src/inline_prompt_editor.rs                               |  6 
crates/agent_ui/src/language_model_selector.rs                            |  5 
crates/agent_ui/src/message_editor.rs                                     | 12 
crates/agent_ui/src/profile_selector.rs                                   |  4 
crates/agent_ui/src/text_thread_editor.rs                                 | 41 
crates/agent_ui/src/ui/agent_notification.rs                              | 16 
crates/agent_ui/src/ui/end_trial_upsell.rs                                |  2 
crates/agent_ui/src/ui/onboarding_modal.rs                                |  4 
crates/agent_ui/src/ui/preview/usage_callouts.rs                          |  2 
crates/ai_onboarding/src/agent_api_keys_onboarding.rs                     |  2 
crates/ai_onboarding/src/ai_onboarding.rs                                 | 20 
crates/ai_onboarding/src/ai_upsell_card.rs                                | 26 
crates/ai_onboarding/src/edit_prediction_onboarding_content.rs            |  1 
crates/assistant_tools/src/find_path_tool.rs                              |  2 
crates/assistant_tools/src/web_search_tool.rs                             |  2 
crates/collab_ui/src/chat_panel.rs                                        |  2 
crates/collab_ui/src/collab_panel.rs                                      |  2 
crates/collab_ui/src/collab_panel/channel_modal.rs                        |  2 
crates/collab_ui/src/notification_panel.rs                                | 32 
crates/collab_ui/src/notifications/incoming_call_notification.rs          |  4 
crates/collab_ui/src/notifications/project_shared_notification.rs         | 10 
crates/collab_ui/src/notifications/stories/collab_notification.rs         |  8 
crates/copilot/src/sign_in.rs                                             | 10 
crates/debugger_tools/src/dap_log.rs                                      |  3 
crates/debugger_ui/src/debugger_panel.rs                                  |  7 
crates/debugger_ui/src/new_process_modal.rs                               | 12 
crates/debugger_ui/src/onboarding_modal.rs                                |  4 
crates/diagnostics/src/diagnostics.rs                                     |  8 
crates/diagnostics/src/items.rs                                           |  2 
crates/editor/src/editor.rs                                               | 10 
crates/extensions_ui/src/components/feature_upsell.rs                     |  2 
crates/extensions_ui/src/extensions_ui.rs                                 | 24 
crates/feedback/src/feedback_modal.rs                                     |  8 
crates/file_finder/src/file_finder.rs                                     |  2 
crates/git_ui/src/blame_ui.rs                                             |  2 
crates/git_ui/src/commit_modal.rs                                         |  2 
crates/git_ui/src/commit_tooltip.rs                                       |  2 
crates/git_ui/src/conflict_view.rs                                        |  6 
crates/git_ui/src/git_panel.rs                                            | 12 
crates/git_ui/src/onboarding.rs                                           |  4 
crates/git_ui/src/project_diff.rs                                         | 14 
crates/go_to_line/src/cursor_position.rs                                  |  2 
crates/image_viewer/src/image_info.rs                                     |  2 
crates/language_models/src/provider/anthropic.rs                          |  5 
crates/language_models/src/provider/bedrock.rs                            |  8 
crates/language_models/src/provider/cloud.rs                              | 33 
crates/language_models/src/provider/copilot_chat.rs                       |  4 
crates/language_models/src/provider/deepseek.rs                           |  4 
crates/language_models/src/provider/google.rs                             |  4 
crates/language_models/src/provider/lmstudio.rs                           |  9 
crates/language_models/src/provider/mistral.rs                            |  5 
crates/language_models/src/provider/ollama.rs                             | 10 
crates/language_models/src/provider/open_ai.rs                            |  7 
crates/language_models/src/provider/open_ai_compatible.rs                 |  2 
crates/language_models/src/provider/open_router.rs                        |  5 
crates/language_models/src/provider/vercel.rs                             |  4 
crates/language_models/src/provider/x_ai.rs                               |  4 
crates/language_models/src/ui/instruction_list_item.rs                    | 14 
crates/language_selector/src/active_buffer_language.rs                    |  2 
crates/language_tools/src/key_context_view.rs                             |  6 
crates/language_tools/src/lsp_log.rs                                      | 53 
crates/notifications/src/status_toast.rs                                  |  2 
crates/onboarding/src/ai_setup_page.rs                                    |  6 
crates/onboarding/src/onboarding.rs                                       |  2 
crates/onboarding/src/welcome.rs                                          |  2 
crates/panel/src/panel.rs                                                 |  8 
crates/project_panel/src/project_panel.rs                                 |  2 
crates/recent_projects/src/disconnected_overlay.rs                        |  4 
crates/recent_projects/src/recent_projects.rs                             |  4 
crates/recent_projects/src/remote_servers.rs                              |  2 
crates/repl/src/components/kernel_options.rs                              |  2 
crates/repl/src/session.rs                                                |  4 
crates/rules_library/src/rules_library.rs                                 | 26 
crates/search/src/project_search.rs                                       | 10 
crates/semantic_index/src/project_index_debug_view.rs                     | 29 
crates/settings_ui/src/keybindings.rs                                     |  6 
crates/settings_ui/src/ui_components/table.rs                             |  6 
crates/tasks_ui/src/modal.rs                                              | 14 
crates/theme_selector/src/icon_theme_selector.rs                          |  4 
crates/theme_selector/src/theme_selector.rs                               |  4 
crates/title_bar/src/application_menu.rs                                  |  5 
crates/title_bar/src/collab.rs                                            |  1 
crates/title_bar/src/title_bar.rs                                         | 14 
crates/toolchain_selector/src/active_toolchain.rs                         |  2 
crates/ui/src/components/banner.rs                                        | 10 
crates/ui/src/components/button/button.rs                                 | 44 
crates/ui/src/components/callout.rs                                       | 20 
crates/ui/src/components/context_menu.rs                                  |  2 
crates/ui/src/components/notification/alert_modal.rs                      |  9 
crates/ui/src/components/settings_container.rs                            |  4 
crates/ui/src/components/tab_bar.rs                                       |  6 
crates/ui/src/components/tooltip.rs                                       |  4 
crates/ui_prompt/src/ui_prompt.rs                                         |  2 
crates/welcome/src/multibuffer_hint.rs                                    |  2 
crates/welcome/src/welcome.rs                                             | 13 
crates/workspace/src/notifications.rs                                     | 16 
crates/workspace/src/theme_preview.rs                                     |  2 
crates/zed/src/zed/component_preview.rs                                   |  2 
crates/zed/src/zed/migrate.rs                                             |  2 
crates/zeta/src/rate_completion_modal.rs                                  |  6 
107 files changed, 524 insertions(+), 417 deletions(-)

Detailed changes

crates/agent_ui/src/acp/thread_view.rs šŸ”—

@@ -1415,7 +1415,7 @@ impl AcpThreadView {
             .text_color(cx.theme().colors().text_muted)
             .child(self.render_markdown(markdown, default_markdown_style(false, window, cx)))
             .child(
-                Button::new(button_id, "Collapse Output")
+                Button::new(button_id, "Collapse Output", cx)
                     .full_width()
                     .style(ButtonStyle::Outlined)
                     .label_size(LabelSize::Small)
@@ -1455,7 +1455,7 @@ impl AcpThreadView {
             .border_color(self.tool_card_border_color(cx))
             .overflow_hidden()
             .child(
-                Button::new(button_id, label)
+                Button::new(button_id, label, cx)
                     .label_size(LabelSize::Small)
                     .color(Color::Muted)
                     .icon(IconName::ArrowUpRight)
@@ -1498,7 +1498,7 @@ impl AcpThreadView {
             )
             .child(h_flex().gap_0p5().children(options.iter().map(|option| {
                 let option_id = SharedString::from(option.id.0.clone());
-                Button::new((option_id, entry_ix), option.name.clone())
+                Button::new((option_id, entry_ix), option.name.clone(), cx)
                     .map(|this| match option.kind {
                         acp::PermissionOptionKind::AllowOnce => {
                             this.icon(IconName::Check).icon_color(Color::Success)
@@ -1628,6 +1628,7 @@ impl AcpThreadView {
                         Button::new(
                             SharedString::from(format!("stop-terminal-{}", terminal.entity_id())),
                             "Stop",
+                            cx,
                         )
                         .icon(IconName::Stop)
                         .icon_position(IconPosition::Start)
@@ -1932,7 +1933,7 @@ impl AcpThreadView {
         {
             let upgrade_message = upgrade_message.clone();
             let upgrade_command = upgrade_command.clone();
-            container = container.child(Button::new("upgrade", upgrade_message).on_click(
+            container = container.child(Button::new("upgrade", upgrade_message, cx).on_click(
                 cx.listener(move |this, _, window, cx| {
                     this.workspace
                         .update(cx, |workspace, cx| {
@@ -2264,7 +2265,7 @@ impl AcpThreadView {
                     )
                     .child(Divider::vertical().color(DividerColor::Border))
                     .child(
-                        Button::new("reject-all-changes", "Reject All")
+                        Button::new("reject-all-changes", "Reject All", cx)
                             .label_size(LabelSize::Small)
                             .disabled(pending_edits)
                             .when(pending_edits, |this| {
@@ -2289,7 +2290,7 @@ impl AcpThreadView {
                             }),
                     )
                     .child(
-                        Button::new("keep-all-changes", "Keep All")
+                        Button::new("keep-all-changes", "Keep All", cx)
                             .label_size(LabelSize::Small)
                             .disabled(pending_edits)
                             .when(pending_edits, |this| {
@@ -2395,7 +2396,7 @@ impl AcpThreadView {
                             .gap_1()
                             .visible_on_hover("edited-code")
                             .child(
-                                Button::new("review", "Review")
+                                Button::new("review", "Review", cx)
                                     .label_size(LabelSize::Small)
                                     .on_click({
                                         let buffer = buffer.clone();
@@ -2406,7 +2407,7 @@ impl AcpThreadView {
                             )
                             .child(Divider::vertical().color(DividerColor::BorderVariant))
                             .child(
-                                Button::new("reject-file", "Reject")
+                                Button::new("reject-file", "Reject", cx)
                                     .label_size(LabelSize::Small)
                                     .disabled(pending_edits)
                                     .on_click({
@@ -2426,7 +2427,7 @@ impl AcpThreadView {
                                     }),
                             )
                             .child(
-                                Button::new("keep-file", "Keep")
+                                Button::new("keep-file", "Keep", cx)
                                     .label_size(LabelSize::Small)
                                     .disabled(pending_edits)
                                     .on_click({
@@ -3114,6 +3115,7 @@ impl Render for AcpThreadView {
                             Button::new(
                                 SharedString::from(method.id.0.clone()),
                                 method.name.clone(),
+                                cx,
                             )
                             .on_click({
                                 let method_id = method.id.clone();

crates/agent_ui/src/active_thread.rs šŸ”—

@@ -2281,7 +2281,7 @@ impl ActiveThread {
                     }
 
                     let restore_checkpoint_button =
-                        Button::new(("restore-checkpoint", ix), "Restore Checkpoint")
+                        Button::new(("restore-checkpoint", ix), "Restore Checkpoint", cx)
                             .icon(if error.is_some() {
                                 IconName::XCircle
                             } else {
@@ -2371,7 +2371,7 @@ impl ActiveThread {
                                         .gap_1()
                                         .justify_end()
                                         .child(
-                                            Button::new("dismiss-feedback-message", "Cancel")
+                                            Button::new("dismiss-feedback-message", "Cancel", cx)
                                                 .label_size(LabelSize::Small)
                                                 .key_binding(
                                                     KeyBinding::for_action_in(
@@ -2394,6 +2394,7 @@ impl ActiveThread {
                                             Button::new(
                                                 "submit-feedback-message",
                                                 "Share Feedback",
+                                                cx,
                                             )
                                             .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                                             .label_size(LabelSize::Small)
@@ -3217,6 +3218,7 @@ impl ActiveThread {
                                             Button::new(
                                                 "always-allow-tool-action",
                                                 "Always Allow",
+                                                cx,
                                             )
                                             .label_size(LabelSize::Small)
                                             .icon(IconName::CheckDouble)
@@ -3254,7 +3256,7 @@ impl ActiveThread {
                                         })
                                         .child({
                                             let tool_id = tool_use.id.clone();
-                                            Button::new("allow-tool-action", "Allow")
+                                            Button::new("allow-tool-action", "Allow", cx)
                                                 .label_size(LabelSize::Small)
                                                 .icon(IconName::Check)
                                                 .icon_position(IconPosition::Start)
@@ -3274,7 +3276,7 @@ impl ActiveThread {
                                         .child({
                                             let tool_id = tool_use.id.clone();
                                             let tool_name: Arc<str> = tool_use.name.into();
-                                            Button::new("deny-tool", "Deny")
+                                            Button::new("deny-tool", "Deny", cx)
                                                 .label_size(LabelSize::Small)
                                                 .icon(IconName::Close)
                                                 .icon_position(IconPosition::Start)

crates/agent_ui/src/agent_configuration.rs šŸ”—

@@ -281,6 +281,7 @@ impl AgentConfiguration {
                             Button::new(
                                 SharedString::from(format!("new-thread-{provider_id}")),
                                 "Start New Thread",
+                                cx,
                             )
                             .icon_position(IconPosition::Start)
                             .icon(IconName::Plus)
@@ -339,7 +340,7 @@ impl AgentConfiguration {
                                     .child(
                                         PopoverMenu::new("add-provider-popover")
                                             .trigger(
-                                                Button::new("add-provider", "Add Provider")
+                                                Button::new("add-provider", "Add Provider", cx)
                                                     .icon_position(IconPosition::Start)
                                                     .icon(IconName::Plus)
                                                     .icon_size(IconSize::Small)
@@ -552,7 +553,7 @@ impl AgentConfiguration {
                     .gap_2()
                     .child(
                         h_flex().w_full().child(
-                            Button::new("add-context-server", "Add Custom Server")
+                            Button::new("add-context-server", "Add Custom Server", cx)
                                 .style(ButtonStyle::Filled)
                                 .layer(ElevationIndex::ModalSurface)
                                 .full_width()
@@ -569,6 +570,7 @@ impl AgentConfiguration {
                             Button::new(
                                 "install-context-server-extensions",
                                 "Install MCP Extensions",
+                                cx,
                             )
                             .style(ButtonStyle::Filled)
                             .layer(ElevationIndex::ModalSurface)

crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs šŸ”—

@@ -281,7 +281,7 @@ impl AddLlmProviderModal {
                     .justify_between()
                     .child(Label::new("Models").size(LabelSize::Small))
                     .child(
-                        Button::new("add-model", "Add Model")
+                        Button::new("add-model", "Add Model", cx)
                             .icon(IconName::Plus)
                             .icon_position(IconPosition::Start)
                             .icon_size(IconSize::XSmall)
@@ -324,7 +324,7 @@ impl AddLlmProviderModal {
             .child(model.max_tokens.clone())
             .when(has_more_than_one_model, |this| {
                 this.child(
-                    Button::new(("remove-model", ix), "Remove Model")
+                    Button::new(("remove-model", ix), "Remove Model", cx)
                         .icon(IconName::Trash)
                         .icon_position(IconPosition::Start)
                         .icon_size(IconSize::XSmall)
@@ -400,7 +400,7 @@ impl Render for AddLlmProviderModal {
                             h_flex()
                                 .gap_1()
                                 .child(
-                                    Button::new("cancel", "Cancel")
+                                    Button::new("cancel", "Cancel", cx)
                                         .key_binding(
                                             KeyBinding::for_action_in(
                                                 &menu::Cancel,
@@ -415,7 +415,7 @@ impl Render for AddLlmProviderModal {
                                         })),
                                 )
                                 .child(
-                                    Button::new("save-server", "Save Provider")
+                                    Button::new("save-server", "Save Provider", cx)
                                         .key_binding(
                                             KeyBinding::for_action_in(
                                                 &menu::Confirm,

crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs šŸ”—

@@ -564,7 +564,7 @@ impl ConfigureContextServerModal {
                 } = &self.source
                 {
                     Some(
-                        Button::new("open-repository", "Open Repository")
+                        Button::new("open-repository", "Open Repository", cx)
                             .icon(IconName::ArrowUpRight)
                             .icon_color(Color::Muted)
                             .icon_size(IconSize::Small)
@@ -600,6 +600,7 @@ impl ConfigureContextServerModal {
                             } else {
                                 "Dismiss"
                             },
+                            cx,
                         )
                         .key_binding(
                             KeyBinding::for_action_in(&menu::Cancel, &focus_handle, window, cx)
@@ -617,6 +618,7 @@ impl ConfigureContextServerModal {
                             } else {
                                 "Configure Server"
                             },
+                            cx,
                         )
                         .disabled(is_connecting)
                         .key_binding(

crates/agent_ui/src/agent_diff.rs šŸ”—

@@ -726,7 +726,7 @@ impl Render for AgentDiffPane {
                         .gap_2()
                         .child("No changes to review")
                         .child(
-                            Button::new("continue-iterating", "Continue Iterating")
+                            Button::new("continue-iterating", "Continue Iterating", cx)
                                 .style(ButtonStyle::Filled)
                                 .icon(IconName::ForwardArrow)
                                 .icon_position(IconPosition::Start)
@@ -806,7 +806,7 @@ fn render_diff_hunk_controls(
         .block_mouse_except_scroll()
         .shadow_md()
         .children(vec![
-            Button::new(("reject", row as u64), "Reject")
+            Button::new(("reject", row as u64), "Reject", cx)
                 .disabled(is_created_file)
                 .key_binding(
                     KeyBinding::for_action_in(
@@ -834,7 +834,7 @@ fn render_diff_hunk_controls(
                         })
                     }
                 }),
-            Button::new(("keep", row as u64), "Keep")
+            Button::new(("keep", row as u64), "Keep", cx)
                 .key_binding(
                     KeyBinding::for_action_in(&Keep, &editor.read(cx).focus_handle(cx), window, cx)
                         .map(|kb| kb.size(rems_from_px(12.))),
@@ -1147,7 +1147,7 @@ impl Render for AgentDiffToolbar {
                         h_flex()
                             .gap_0p5()
                             .child(
-                                Button::new("reject-all", "Reject All")
+                                Button::new("reject-all", "Reject All", cx)
                                     .key_binding({
                                         KeyBinding::for_action_in(
                                             &RejectAll,
@@ -1162,7 +1162,7 @@ impl Render for AgentDiffToolbar {
                                     })),
                             )
                             .child(
-                                Button::new("keep-all", "Keep All")
+                                Button::new("keep-all", "Keep All", cx)
                                     .key_binding({
                                         KeyBinding::for_action_in(
                                             &KeepAll,
@@ -1242,7 +1242,7 @@ impl Render for AgentDiffToolbar {
                     .child(
                         h_group_sm()
                             .child(
-                                Button::new("reject-all", "Reject All")
+                                Button::new("reject-all", "Reject All", cx)
                                     .key_binding({
                                         KeyBinding::for_action_in(
                                             &RejectAll,
@@ -1257,7 +1257,7 @@ impl Render for AgentDiffToolbar {
                                     })),
                             )
                             .child(
-                                Button::new("keep-all", "Keep All")
+                                Button::new("keep-all", "Keep All", cx)
                                     .key_binding({
                                         KeyBinding::for_action_in(
                                             &KeepAll,

crates/agent_ui/src/agent_panel.rs šŸ”—

@@ -2444,7 +2444,7 @@ impl AgentPanel {
                                         .gap_1()
                                         .max_w_48()
                                         .child(
-                                            Button::new("context", "Add Context")
+                                            Button::new("context", "Add Context", cx)
                                                 .label_size(LabelSize::Small)
                                                 .icon(IconName::FileCode)
                                                 .icon_position(IconPosition::Start)
@@ -2465,7 +2465,7 @@ impl AgentPanel {
                                                 }),
                                         )
                                         .child(
-                                            Button::new("mode", "Switch Model")
+                                            Button::new("mode", "Switch Model", cx)
                                                 .label_size(LabelSize::Small)
                                                 .icon(IconName::DatabaseZap)
                                                 .icon_position(IconPosition::Start)
@@ -2486,7 +2486,7 @@ impl AgentPanel {
                                                 }),
                                         )
                                         .child(
-                                            Button::new("settings", "View Settings")
+                                            Button::new("settings", "View Settings", cx)
                                                 .label_size(LabelSize::Small)
                                                 .icon(IconName::Settings)
                                                 .icon_position(IconPosition::Start)
@@ -2529,7 +2529,7 @@ impl AgentPanel {
                         self.render_empty_state_section_header(
                             "Recent",
                             Some(
-                                Button::new("view-history", "View All")
+                                Button::new("view-history", "View All", cx)
                                     .style(ButtonStyle::Subtle)
                                     .label_size(LabelSize::Small)
                                     .key_binding(
@@ -2727,7 +2727,7 @@ impl AgentPanel {
                 .severity(ui::Severity::Warning)
                 .child(Label::new(configuration_error.to_string()))
                 .action_slot(
-                    Button::new("settings", "Configure Provider")
+                    Button::new("settings", "Configure Provider", cx)
                         .style(ButtonStyle::Tinted(ui::TintColor::Warning))
                         .label_size(LabelSize::Small)
                         .key_binding(
@@ -2784,7 +2784,7 @@ impl AgentPanel {
                 h_flex()
                     .gap_1()
                     .child(
-                        Button::new("continue-conversation", "Continue")
+                        Button::new("continue-conversation", "Continue", cx)
                             .layer(ElevationIndex::ModalSurface)
                             .label_size(LabelSize::Small)
                             .key_binding(
@@ -2802,7 +2802,7 @@ impl AgentPanel {
                     )
                     .when(model.supports_burn_mode(), |this| {
                         this.child(
-                            Button::new("continue-burn-mode", "Continue with Burn Mode")
+                            Button::new("continue-burn-mode", "Continue with Burn Mode", cx)
                                 .style(ButtonStyle::Filled)
                                 .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                                 .layer(ElevationIndex::ModalSurface)
@@ -2873,7 +2873,7 @@ impl AgentPanel {
         thread: &Entity<ActiveThread>,
         cx: &mut Context<Self>,
     ) -> impl IntoElement {
-        Button::new("upgrade", "Upgrade")
+        Button::new("upgrade", "Upgrade", cx)
             .label_size(LabelSize::Small)
             .style(ButtonStyle::Tinted(ui::TintColor::Accent))
             .on_click(cx.listener({
@@ -2965,7 +2965,7 @@ impl AgentPanel {
             .size(IconSize::Small)
             .color(Color::Error);
 
-        let retry_button = Button::new("retry", "Retry")
+        let retry_button = Button::new("retry", "Retry", cx)
             .icon(IconName::RotateCw)
             .icon_position(IconPosition::Start)
             .icon_size(IconSize::Small)
@@ -3009,7 +3009,7 @@ impl AgentPanel {
             .size(IconSize::Small)
             .color(Color::Error);
 
-        let retry_button = Button::new("retry", "Retry")
+        let retry_button = Button::new("retry", "Retry", cx)
             .icon(IconName::RotateCw)
             .icon_position(IconPosition::Start)
             .icon_size(IconSize::Small)
@@ -3034,22 +3034,26 @@ impl AgentPanel {
             .primary_action(retry_button);
 
         if can_enable_burn_mode {
-            let burn_mode_button = Button::new("enable_burn_retry", "Enable Burn Mode and Retry")
-                .icon(IconName::ZedBurnMode)
-                .icon_position(IconPosition::Start)
-                .icon_size(IconSize::Small)
-                .label_size(LabelSize::Small)
-                .on_click({
-                    let thread = thread.clone();
-                    move |_, window, cx| {
-                        thread.update(cx, |thread, cx| {
-                            thread.clear_last_error();
-                            thread.thread().update(cx, |thread, cx| {
-                                thread.enable_burn_mode_and_retry(Some(window.window_handle()), cx);
+            let burn_mode_button =
+                Button::new("enable_burn_retry", "Enable Burn Mode and Retry", cx)
+                    .icon(IconName::ZedBurnMode)
+                    .icon_position(IconPosition::Start)
+                    .icon_size(IconSize::Small)
+                    .label_size(LabelSize::Small)
+                    .on_click({
+                        let thread = thread.clone();
+                        move |_, window, cx| {
+                            thread.update(cx, |thread, cx| {
+                                thread.clear_last_error();
+                                thread.thread().update(cx, |thread, cx| {
+                                    thread.enable_burn_mode_and_retry(
+                                        Some(window.window_handle()),
+                                        cx,
+                                    );
+                                });
                             });
-                        });
-                    }
-                });
+                        }
+                    });
             callout = callout.secondary_action(burn_mode_button);
         }
 

crates/agent_ui/src/inline_prompt_editor.rs šŸ”—

@@ -478,7 +478,7 @@ impl<T: 'static> PromptEditor<T> {
         match codegen_status {
             CodegenStatus::Idle => {
                 vec![
-                    Button::new("start", mode.start_label())
+                    Button::new("start", mode.start_label(), cx)
                         .label_size(LabelSize::Small)
                         .icon(IconName::Return)
                         .icon_size(IconSize::XSmall)
@@ -745,11 +745,11 @@ impl<T: 'static> PromptEditor<T> {
                             h_flex()
                                 .gap_2()
                                 .child(
-                                    Button::new("dismiss", "Dismiss")
+                                    Button::new("dismiss", "Dismiss", cx)
                                         .style(ButtonStyle::Transparent)
                                         .on_click(cx.listener(Self::toggle_rate_limit_notice)),
                                 )
-                                .child(Button::new("more-info", "More Info").on_click(
+                                .child(Button::new("more-info", "More Info", cx).on_click(
                                     |_event, window, cx| {
                                         window.dispatch_action(
                                             Box::new(zed_actions::OpenAccountSettings),

crates/agent_ui/src/language_model_selector.rs šŸ”—

@@ -548,7 +548,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
                 .justify_between()
                 .when(cx.has_flag::<ZedProFeatureFlag>(), |this| {
                     this.child(match plan {
-                        Plan::ZedPro => Button::new("zed-pro", "Zed Pro")
+                        Plan::ZedPro => Button::new("zed-pro", "Zed Pro", cx)
                             .icon(IconName::ZedAssistant)
                             .icon_size(IconSize::Small)
                             .icon_color(Color::Muted)
@@ -564,12 +564,13 @@ impl PickerDelegate for LanguageModelPickerDelegate {
                             } else {
                                 "Try Pro"
                             },
+                            cx,
                         )
                         .on_click(|_, _, cx| cx.open_url(TRY_ZED_PRO_URL)),
                     })
                 })
                 .child(
-                    Button::new("configure", "Configure")
+                    Button::new("configure", "Configure", cx)
                         .icon(IconName::Settings)
                         .icon_size(IconSize::Small)
                         .icon_color(Color::Muted)

crates/agent_ui/src/message_editor.rs šŸ”—

@@ -1074,7 +1074,7 @@ impl MessageEditor {
                             )
                             .child(Divider::vertical().color(DividerColor::Border))
                             .child(
-                                Button::new("reject-all-changes", "Reject All")
+                                Button::new("reject-all-changes", "Reject All", cx)
                                     .label_size(LabelSize::Small)
                                     .disabled(pending_edits)
                                     .when(pending_edits, |this| {
@@ -1094,7 +1094,7 @@ impl MessageEditor {
                                     })),
                             )
                             .child(
-                                Button::new("accept-all-changes", "Accept All")
+                                Button::new("accept-all-changes", "Accept All", cx)
                                     .label_size(LabelSize::Small)
                                     .disabled(pending_edits)
                                     .when(pending_edits, |this| {
@@ -1204,7 +1204,7 @@ impl MessageEditor {
                                         .gap_1()
                                         .visible_on_hover("edited-code")
                                         .child(
-                                            Button::new("review", "Review")
+                                            Button::new("review", "Review", cx)
                                                 .label_size(LabelSize::Small)
                                                 .on_click({
                                                     let buffer = buffer.clone();
@@ -1221,7 +1221,7 @@ impl MessageEditor {
                                             Divider::vertical().color(DividerColor::BorderVariant),
                                         )
                                         .child(
-                                            Button::new("reject-file", "Reject")
+                                            Button::new("reject-file", "Reject", cx)
                                                 .label_size(LabelSize::Small)
                                                 .disabled(pending_edits)
                                                 .on_click({
@@ -1236,7 +1236,7 @@ impl MessageEditor {
                                                 }),
                                         )
                                         .child(
-                                            Button::new("accept-file", "Accept")
+                                            Button::new("accept-file", "Accept", cx)
                                                 .label_size(LabelSize::Small)
                                                 .disabled(pending_edits)
                                                 .on_click({
@@ -1332,7 +1332,7 @@ impl MessageEditor {
             .title(title)
             .description(description)
             .primary_action(
-                Button::new("start-new-thread", "Start New Thread")
+                Button::new("start-new-thread", "Start New Thread", cx)
                     .label_size(LabelSize::Small)
                     .on_click(cx.listener(|this, _, window, cx| {
                         let from_thread_id = Some(this.thread.read(cx).id().clone());

crates/agent_ui/src/profile_selector.rs šŸ”—

@@ -167,7 +167,7 @@ impl Render for ProfileSelector {
         if configured_model.model.supports_tools() {
             let this = cx.entity().clone();
             let focus_handle = self.focus_handle.clone();
-            let trigger_button = Button::new("profile-selector-model", selected_profile)
+            let trigger_button = Button::new("profile-selector-model", selected_profile, cx)
                 .label_size(LabelSize::Small)
                 .color(Color::Muted)
                 .icon(IconName::ChevronDown)
@@ -201,7 +201,7 @@ impl Render for ProfileSelector {
                 })
                 .into_any_element()
         } else {
-            Button::new("tools-not-supported-button", "Tools Unsupported")
+            Button::new("tools-not-supported-button", "Tools Unsupported", cx)
                 .disabled(true)
                 .label_size(LabelSize::Small)
                 .color(Color::Muted)

crates/agent_ui/src/text_thread_editor.rs šŸ”—

@@ -1200,7 +1200,7 @@ impl TextThreadEditor {
                             })
                             .children(match &message.status {
                                 MessageStatus::Error(error) => Some(
-                                    Button::new("show-error", "Error")
+                                    Button::new("show-error", "Error", cx)
                                         .color(Color::Error)
                                         .selected_label_color(Color::Error)
                                         .selected_icon_color(Color::Error)
@@ -1920,7 +1920,7 @@ impl TextThreadEditor {
             None => (ButtonStyle::Filled, None),
         };
 
-        Button::new("send_button", "Send")
+        Button::new("send_button", "Send", cx)
             .label_size(LabelSize::Small)
             .disabled(self.sending_disabled(cx))
             .style(style)
@@ -2124,14 +2124,16 @@ impl TextThreadEditor {
                 h_flex()
                     .justify_end()
                     .mt_1()
-                    .child(Button::new("subscribe", "Subscribe").on_click(cx.listener(
-                        |this, _, _window, cx| {
-                            this.last_error = None;
-                            cx.open_url(&zed_urls::account_url(cx));
-                            cx.notify();
-                        },
-                    )))
-                    .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
+                    .child(
+                        Button::new("subscribe", "Subscribe", cx).on_click(cx.listener(
+                            |this, _, _window, cx| {
+                                this.last_error = None;
+                                cx.open_url(&zed_urls::account_url(cx));
+                                cx.notify();
+                            },
+                        )),
+                    )
+                    .child(Button::new("dismiss", "Dismiss", cx).on_click(cx.listener(
                         |this, _, _window, cx| {
                             this.last_error = None;
                             cx.notify();
@@ -2165,17 +2167,14 @@ impl TextThreadEditor {
                     .overflow_y_scroll()
                     .child(Label::new(error_message.clone())),
             )
-            .child(
-                h_flex()
-                    .justify_end()
-                    .mt_1()
-                    .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
-                        |this, _, _window, cx| {
-                            this.last_error = None;
-                            cx.notify();
-                        },
-                    ))),
-            )
+            .child(h_flex().justify_end().mt_1().child(
+                Button::new("dismiss", "Dismiss", cx).on_click(cx.listener(
+                    |this, _, _window, cx| {
+                        this.last_error = None;
+                        cx.notify();
+                    },
+                )),
+            ))
             .into_any()
     }
 }

crates/agent_ui/src/ui/agent_notification.rs šŸ”—

@@ -171,7 +171,7 @@ impl Render for AgentNotification {
                     .gap_1()
                     .items_center()
                     .child(
-                        Button::new("open", "View Panel")
+                        Button::new("open", "View Panel", cx)
                             .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                             .full_width()
                             .on_click({
@@ -180,11 +180,15 @@ impl Render for AgentNotification {
                                 })
                             }),
                     )
-                    .child(Button::new("dismiss", "Dismiss").full_width().on_click({
-                        cx.listener(move |_, _event, _, cx| {
-                            cx.emit(AgentNotificationEvent::Dismissed);
-                        })
-                    })),
+                    .child(
+                        Button::new("dismiss", "Dismiss", cx)
+                            .full_width()
+                            .on_click({
+                                cx.listener(move |_, _event, _, cx| {
+                                    cx.emit(AgentNotificationEvent::Dismissed);
+                                })
+                            }),
+                    ),
             )
     }
 }

crates/agent_ui/src/ui/end_trial_upsell.rs šŸ”—

@@ -35,7 +35,7 @@ impl RenderOnce for EndTrialUpsell {
             )
             .child(plan_definitions.pro_plan(false))
             .child(
-                Button::new("cta-button", "Upgrade to Zed Pro")
+                Button::new("cta-button", "Upgrade to Zed Pro", cx)
                     .full_width()
                     .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                     .on_click(move |_, _window, cx| {

crates/agent_ui/src/ui/onboarding_modal.rs šŸ”—

@@ -147,13 +147,13 @@ impl Render for AgentOnboardingModal {
                 )),
             ));
 
-        let open_panel_button = Button::new("open-panel", "Get Started with the Agent Panel")
+        let open_panel_button = Button::new("open-panel", "Get Started with the Agent Panel", cx)
             .icon_size(IconSize::Indicator)
             .style(ButtonStyle::Tinted(TintColor::Accent))
             .full_width()
             .on_click(cx.listener(Self::open_panel));
 
-        let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
+        let blog_post_button = Button::new("view-blog", "Check out the Blog Post", cx)
             .icon(IconName::ArrowUpRight)
             .icon_size(IconSize::Indicator)
             .icon_color(Color::Muted)

crates/agent_ui/src/ui/preview/usage_callouts.rs šŸ”—

@@ -99,7 +99,7 @@ impl RenderOnce for UsageCallout {
                     .title(title)
                     .description(message)
                     .primary_action(
-                        Button::new("upgrade", button_text)
+                        Button::new("upgrade", button_text, cx)
                             .label_size(LabelSize::Small)
                             .on_click(move |_, _, cx| {
                                 cx.open_url(&url);

crates/ai_onboarding/src/agent_api_keys_onboarding.rs šŸ”—

@@ -130,7 +130,7 @@ impl RenderOnce for ApiKeysWithoutProviders {
                 "Add your own keys to use AI without signing in.",
             )))
             .child(
-                Button::new("configure-providers", "Configure Providers")
+                Button::new("configure-providers", "Configure Providers", cx)
                     .full_width()
                     .style(ButtonStyle::Outlined)
                     .on_click(move |_, window, cx| {

crates/ai_onboarding/src/ai_onboarding.rs šŸ”—

@@ -94,7 +94,7 @@ impl ZedAiOnboarding {
         self
     }
 
-    fn render_accept_terms_of_service(&self) -> AnyElement {
+    fn render_accept_terms_of_service(&self, cx: &mut App) -> AnyElement {
         v_flex()
             .gap_1()
             .w_full()
@@ -105,7 +105,7 @@ impl ZedAiOnboarding {
                     .mb_2(),
             )
             .child(
-                Button::new("terms_of_service", "Review Terms of Service")
+                Button::new("terms_of_service", "Review Terms of Service", cx)
                     .full_width()
                     .style(ButtonStyle::Outlined)
                     .icon(IconName::ArrowUpRight)
@@ -117,7 +117,7 @@ impl ZedAiOnboarding {
                     }),
             )
             .child(
-                Button::new("accept_terms", "Accept")
+                Button::new("accept_terms", "Accept", cx)
                     .full_width()
                     .style(ButtonStyle::Tinted(TintColor::Accent))
                     .on_click({
@@ -130,7 +130,7 @@ impl ZedAiOnboarding {
             .into_any_element()
     }
 
-    fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
+    fn render_sign_in_disclaimer(&self, cx: &mut App) -> AnyElement {
         let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
         let plan_definitions = PlanDefinitions;
 
@@ -144,7 +144,7 @@ impl ZedAiOnboarding {
             )
             .child(plan_definitions.pro_plan(false))
             .child(
-                Button::new("sign_in", "Try Zed Pro for Free")
+                Button::new("sign_in", "Try Zed Pro for Free", cx)
                     .disabled(signing_in)
                     .full_width()
                     .style(ButtonStyle::Tinted(ui::TintColor::Accent))
@@ -187,7 +187,7 @@ impl ZedAiOnboarding {
                         )
                         .child(plan_definitions.pro_plan(true))
                         .child(
-                            Button::new("pro", "Get Started")
+                            Button::new("pro", "Get Started", cx)
                                 .full_width()
                                 .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                                 .on_click(move |_, _window, cx| {
@@ -268,7 +268,7 @@ impl ZedAiOnboarding {
                         )
                         .child(plan_definitions.pro_trial(true))
                         .child(
-                            Button::new("pro", "Start Free Trial")
+                            Button::new("pro", "Start Free Trial", cx)
                                 .full_width()
                                 .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                                 .on_click(move |_, _window, cx| {
@@ -320,7 +320,7 @@ impl ZedAiOnboarding {
             .into_any_element()
     }
 
-    fn render_pro_plan_state(&self, _cx: &mut App) -> AnyElement {
+    fn render_pro_plan_state(&self, cx: &mut App) -> AnyElement {
         let plan_definitions = PlanDefinitions;
 
         v_flex()
@@ -333,7 +333,7 @@ impl ZedAiOnboarding {
             )
             .child(plan_definitions.pro_plan(false))
             .child(
-                Button::new("pro", "Continue with Zed Pro")
+                Button::new("pro", "Continue with Zed Pro", cx)
                     .full_width()
                     .style(ButtonStyle::Outlined)
                     .on_click({
@@ -358,7 +358,7 @@ impl RenderOnce for ZedAiOnboarding {
                     Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
                 }
             } else {
-                self.render_accept_terms_of_service()
+                self.render_accept_terms_of_service(cx)
             }
         } else {
             self.render_sign_in_disclaimer(cx)

crates/ai_onboarding/src/ai_upsell_card.rs šŸ”—

@@ -186,7 +186,7 @@ impl RenderOnce for AiUpsellCard {
                                     )
                                     .child(plan_definitions.pro_plan(true))
                                     .child(
-                                        Button::new("pro", "Get Started")
+                                        Button::new("pro", "Get Started", cx)
                                             .full_width()
                                             .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                                             .on_click(move |_, _window, cx| {
@@ -209,19 +209,25 @@ impl RenderOnce for AiUpsellCard {
                             .child(
                                 footer_container
                                     .child(
-                                        Button::new("start_trial", "Start 14-day Free Pro Trial")
-                                            .full_width()
-                                            .style(ButtonStyle::Tinted(ui::TintColor::Accent))
-                                            .when_some(self.tab_index, |this, tab_index| {
-                                                this.tab_index(tab_index)
-                                            })
-                                            .on_click(move |_, _window, cx| {
+                                        Button::new(
+                                            "start_trial",
+                                            "Start 14-day Free Pro Trial",
+                                            cx,
+                                        )
+                                        .full_width()
+                                        .style(ButtonStyle::Tinted(ui::TintColor::Accent))
+                                        .when_some(self.tab_index, |this, tab_index| {
+                                            this.tab_index(tab_index)
+                                        })
+                                        .on_click(
+                                            move |_, _window, cx| {
                                                 telemetry::event!(
                                                     "Start Trial Clicked",
                                                     state = "post-sign-in"
                                                 );
                                                 cx.open_url(&zed_urls::start_trial_url(cx))
-                                            }),
+                                            },
+                                        ),
                                     )
                                     .child(
                                         Label::new("No credit card required")
@@ -261,7 +267,7 @@ impl RenderOnce for AiUpsellCard {
                 )
                 .child(plans_section)
                 .child(
-                    Button::new("sign_in", "Sign In")
+                    Button::new("sign_in", "Sign In", cx)
                         .full_width()
                         .style(ButtonStyle::Tinted(ui::TintColor::Accent))
                         .when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index))

crates/assistant_tools/src/find_path_tool.rs šŸ”—

@@ -256,7 +256,7 @@ impl ToolCard for FindPathToolCard {
                         let workspace_clone = workspace.clone();
                         let button_label = path.to_string_lossy().to_string();
 
-                        Button::new(("path", index), button_label)
+                        Button::new(("path", index), button_label, cx)
                             .icon(IconName::ArrowUpRight)
                             .icon_size(IconSize::Small)
                             .icon_position(IconPosition::End)

crates/assistant_tools/src/web_search_tool.rs šŸ”—

@@ -174,7 +174,7 @@ impl ToolCard for WebSearchToolCard {
                         let title = result.title.clone();
                         let url = SharedString::from(result.url.clone());
 
-                        Button::new(("result", index), title)
+                        Button::new(("result", index), title, cx)
                             .label_size(LabelSize::Small)
                             .color(Color::Muted)
                             .icon(IconName::ArrowUpRight)

crates/collab_ui/src/chat_panel.rs šŸ”—

@@ -987,7 +987,7 @@ impl Render for ChatPanel {
                             )
                             .child(
                                 div().pt_1().w_full().items_center().child(
-                                    Button::new("toggle-collab", "Open")
+                                    Button::new("toggle-collab", "Open", cx)
                                         .full_width()
                                         .key_binding(KeyBinding::for_action(
                                             &collab_panel::ToggleFocus,

crates/collab_ui/src/collab_panel.rs šŸ”—

@@ -2307,7 +2307,7 @@ impl CollabPanel {
                 v_flex()
                     .gap_2()
                     .child(
-                        Button::new("sign_in", "Sign in")
+                        Button::new("sign_in", "Sign in", cx)
                             .icon_color(Color::Muted)
                             .icon(IconName::Github)
                             .icon_position(IconPosition::Start)

crates/collab_ui/src/collab_panel/channel_modal.rs šŸ”—

@@ -177,7 +177,7 @@ impl Render for ChannelModal {
                             ))
                             .children(
                                 Some(
-                                    Button::new("copy-link", "Copy Link")
+                                    Button::new("copy-link", "Copy Link", cx)
                                         .label_size(LabelSize::Small)
                                         .on_click(cx.listener(move |this, _, _, cx| {
                                             if let Some(channel) = this

crates/collab_ui/src/notification_panel.rs šŸ”—

@@ -319,20 +319,22 @@ impl NotificationPanel {
                                         h_flex()
                                             .flex_grow()
                                             .justify_end()
-                                            .child(Button::new("decline", "Decline").on_click({
-                                                let notification = notification.clone();
-                                                let entity = cx.entity().clone();
-                                                move |_, _, cx| {
-                                                    entity.update(cx, |this, cx| {
-                                                        this.respond_to_notification(
-                                                            notification.clone(),
-                                                            false,
-                                                            cx,
-                                                        )
-                                                    });
-                                                }
-                                            }))
-                                            .child(Button::new("accept", "Accept").on_click({
+                                            .child(Button::new("decline", "Decline", cx).on_click(
+                                                {
+                                                    let notification = notification.clone();
+                                                    let entity = cx.entity().clone();
+                                                    move |_, _, cx| {
+                                                        entity.update(cx, |this, cx| {
+                                                            this.respond_to_notification(
+                                                                notification.clone(),
+                                                                false,
+                                                                cx,
+                                                            )
+                                                        });
+                                                    }
+                                                },
+                                            ))
+                                            .child(Button::new("accept", "Accept", cx).on_click({
                                                 let notification = notification.clone();
                                                 let entity = cx.entity().clone();
                                                 move |_, _, cx| {
@@ -631,7 +633,7 @@ impl Render for NotificationPanel {
                             .gap_2()
                             .p_4()
                             .child(
-                                Button::new("connect_prompt_button", "Connect")
+                                Button::new("connect_prompt_button", "Connect", cx)
                                     .icon_color(Color::Muted)
                                     .icon(IconName::Github)
                                     .icon_position(IconPosition::Start)

crates/collab_ui/src/notifications/incoming_call_notification.rs šŸ”—

@@ -117,11 +117,11 @@ impl Render for IncomingCallNotification {
         div().size_full().font(ui_font).child(
             CollabNotification::new(
                 self.state.call.calling_user.avatar_uri.clone(),
-                Button::new("accept", "Accept").on_click({
+                Button::new("accept", "Accept", cx).on_click({
                     let state = self.state.clone();
                     move |_, _, cx| state.respond(true, cx)
                 }),
-                Button::new("decline", "Decline").on_click({
+                Button::new("decline", "Decline", cx).on_click({
                     let state = self.state.clone();
                     move |_, _, cx| state.respond(false, cx)
                 }),

crates/collab_ui/src/notifications/project_shared_notification.rs šŸ”—

@@ -126,10 +126,12 @@ impl Render for ProjectSharedNotification {
         div().size_full().font(ui_font).child(
             CollabNotification::new(
                 self.owner.avatar_uri.clone(),
-                Button::new("open", "Open").on_click(cx.listener(move |this, _event, _, cx| {
-                    this.join(cx);
-                })),
-                Button::new("dismiss", "Dismiss").on_click(cx.listener(
+                Button::new("open", "Open", cx).on_click(cx.listener(
+                    move |this, _event, _, cx| {
+                        this.join(cx);
+                    },
+                )),
+                Button::new("dismiss", "Dismiss", cx).on_click(cx.listener(
                     move |this, _event, _, cx| {
                         this.dismiss(cx);
                     },

crates/collab_ui/src/notifications/stories/collab_notification.rs šŸ”—

@@ -18,8 +18,8 @@ impl Render for CollabNotificationStory {
                     window_container(400., 72.).child(
                         CollabNotification::new(
                             "https://avatars.githubusercontent.com/u/1486634?v=4",
-                            Button::new("accept", "Accept"),
-                            Button::new("decline", "Decline"),
+                            Button::new("accept", "Accept", cx),
+                            Button::new("decline", "Decline", cx),
                         )
                         .child(
                             v_flex()
@@ -35,8 +35,8 @@ impl Render for CollabNotificationStory {
                     window_container(400., 72.).child(
                         CollabNotification::new(
                             "https://avatars.githubusercontent.com/u/1714999?v=4",
-                            Button::new("open", "Open"),
-                            Button::new("dismiss", "Dismiss"),
+                            Button::new("open", "Open", cx),
+                            Button::new("dismiss", "Dismiss", cx),
                         )
                         .child(Label::new("iamnbutler"))
                         .child(Label::new("is sharing a project in Zed:"))

crates/copilot/src/sign_in.rs šŸ”—

@@ -264,7 +264,7 @@ impl CopilotCodeVerification {
                     .size(ui::LabelSize::Small),
             )
             .child(
-                Button::new("connect-button", connect_button_label)
+                Button::new("connect-button", connect_button_label, cx)
                     .on_click({
                         let verification_uri = data.verification_uri.clone();
                         cx.listener(move |this, _, _window, cx| {
@@ -276,7 +276,7 @@ impl CopilotCodeVerification {
                     .style(ButtonStyle::Filled),
             )
             .child(
-                Button::new("copilot-enable-cancel-button", "Cancel")
+                Button::new("copilot-enable-cancel-button", "Cancel", cx)
                     .full_width()
                     .on_click(cx.listener(|_, _, _, cx| {
                         cx.emit(DismissEvent);
@@ -292,7 +292,7 @@ impl CopilotCodeVerification {
                 "You can update your settings or sign out from the Copilot menu in the status bar.",
             ))
             .child(
-                Button::new("copilot-enabled-done-button", "Done")
+                Button::new("copilot-enabled-done-button", "Done", cx)
                     .full_width()
                     .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )
@@ -306,12 +306,12 @@ impl CopilotCodeVerification {
                 "You can enable Copilot by connecting your existing license once you have subscribed or renewed your subscription.",
             ).color(Color::Warning))
             .child(
-                Button::new("copilot-subscribe-button", "Subscribe on GitHub")
+                Button::new("copilot-subscribe-button", "Subscribe on GitHub", cx)
                     .full_width()
                     .on_click(|_, _, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
             )
             .child(
-                Button::new("copilot-subscribe-cancel-button", "Cancel")
+                Button::new("copilot-subscribe-cancel-button", "Cancel", cx)
                     .full_width()
                     .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )

crates/debugger_tools/src/dap_log.rs šŸ”—

@@ -535,6 +535,7 @@ impl Render for DapLogToolbarItemView {
                         ))
                     })
                     .unwrap_or_else(|| "No adapter selected".into()),
+                cx,
             ))
             .menu(move |mut window, cx| {
                 let log_view = log_view.clone();
@@ -632,7 +633,7 @@ impl Render for DapLogToolbarItemView {
             .child(
                 div()
                     .child(
-                        Button::new("clear_log_button", "Clear").on_click(cx.listener(
+                        Button::new("clear_log_button", "Clear", cx).on_click(cx.listener(
                             |this, _, window, cx| {
                                 if let Some(log_view) = this.log_view.as_ref() {
                                     log_view.update(cx, |log_view, cx| {

crates/debugger_ui/src/debugger_panel.rs šŸ”—

@@ -1715,7 +1715,7 @@ impl Render for DebugPanel {
                         .justify_center()
                         .gap_2()
                         .child(
-                            Button::new("spawn-new-session-empty-state", "New Session")
+                            Button::new("spawn-new-session-empty-state", "New Session", cx)
                                 .icon(IconName::Plus)
                                 .icon_size(IconSize::XSmall)
                                 .icon_color(Color::Muted)
@@ -1725,7 +1725,7 @@ impl Render for DebugPanel {
                                 }),
                         )
                         .child(
-                            Button::new("edit-debug-settings", "Edit debug.json")
+                            Button::new("edit-debug-settings", "Edit debug.json", cx)
                                 .icon(IconName::Code)
                                 .icon_size(IconSize::XSmall)
                                 .color(Color::Muted)
@@ -1739,7 +1739,7 @@ impl Render for DebugPanel {
                                 }),
                         )
                         .child(
-                            Button::new("open-debugger-docs", "Debugger Docs")
+                            Button::new("open-debugger-docs", "Debugger Docs", cx)
                                 .icon(IconName::Book)
                                 .color(Color::Muted)
                                 .icon_size(IconSize::XSmall)
@@ -1751,6 +1751,7 @@ impl Render for DebugPanel {
                             Button::new(
                                 "spawn-new-session-install-extensions",
                                 "Debugger Extensions",
+                                cx,
                             )
                             .icon(IconName::Blocks)
                             .color(Color::Muted)

crates/debugger_ui/src/new_process_modal.rs šŸ”—

@@ -703,7 +703,7 @@ impl Render for NewProcessModal {
                         container
                             .child(
                                 h_flex().child(
-                                    Button::new("edit-custom-debug", "Edit in debug.json")
+                                    Button::new("edit-custom-debug", "Edit in debug.json", cx)
                                         .on_click(cx.listener(|this, _, window, cx| {
                                             this.save_debug_scenario(window, cx);
                                         }))
@@ -719,7 +719,7 @@ impl Render for NewProcessModal {
                                 ),
                             )
                             .child(
-                                Button::new("debugger-spawn", "Start")
+                                Button::new("debugger-spawn", "Start", cx)
                                     .on_click(cx.listener(|this, _, window, cx| {
                                         this.start_new_session(window, cx)
                                     }))
@@ -751,7 +751,7 @@ impl Render for NewProcessModal {
                             .child(div().children(
                                 KeyBinding::for_action(&*secondary_action, window, cx).map(
                                     |keybind| {
-                                        Button::new("edit-attach-task", "Edit in debug.json")
+                                        Button::new("edit-attach-task", "Edit in debug.json", cx)
                                             .label_size(LabelSize::Small)
                                             .key_binding(keybind)
                                             .on_click(move |_, window, cx| {
@@ -1389,7 +1389,7 @@ impl PickerDelegate for DebugDelegate {
             .children({
                 let action = menu::SecondaryConfirm.boxed_clone();
                 KeyBinding::for_action(&*action, window, cx).map(|keybind| {
-                    Button::new("edit-debug-task", "Edit in debug.json")
+                    Button::new("edit-debug-task", "Edit in debug.json", cx)
                         .label_size(LabelSize::Small)
                         .key_binding(keybind)
                         .on_click(move |_, window, cx| {
@@ -1401,7 +1401,7 @@ impl PickerDelegate for DebugDelegate {
                 if (current_modifiers.alt || self.matches.is_empty()) && !self.prompt.is_empty() {
                     let action = picker::ConfirmInput { secondary: false }.boxed_clone();
                     this.children(KeyBinding::for_action(&*action, window, cx).map(|keybind| {
-                        Button::new("launch-custom", "Launch Custom")
+                        Button::new("launch-custom", "Launch Custom", cx)
                             .key_binding(keybind)
                             .on_click(move |_, window, cx| {
                                 window.dispatch_action(action.boxed_clone(), cx)
@@ -1415,7 +1415,7 @@ impl PickerDelegate for DebugDelegate {
                             let run_entry_label =
                                 if is_recent_selected { "Rerun" } else { "Spawn" };
 
-                            Button::new("spawn", run_entry_label)
+                            Button::new("spawn", run_entry_label, cx)
                                 .key_binding(keybind)
                                 .on_click(|_, window, cx| {
                                     window.dispatch_action(menu::Confirm.boxed_clone(), cx);

crates/debugger_ui/src/onboarding_modal.rs šŸ”—

@@ -139,13 +139,13 @@ impl Render for DebuggerOnboardingModal {
                 )),
             ));
 
-        let open_panel_button = Button::new("open-panel", "Get Started with the Debugger")
+        let open_panel_button = Button::new("open-panel", "Get Started with the Debugger", cx)
             .icon_size(IconSize::Indicator)
             .style(ButtonStyle::Tinted(TintColor::Accent))
             .full_width()
             .on_click(cx.listener(Self::open_panel));
 
-        let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
+        let blog_post_button = Button::new("view-blog", "Check out the Blog Post", cx)
             .icon(IconName::ArrowUpRight)
             .icon_size(IconSize::Indicator)
             .icon_color(Color::Muted)

crates/diagnostics/src/diagnostics.rs šŸ”—

@@ -128,12 +128,12 @@ impl Render for ProjectDiagnosticsEditor {
                         self.summary.warning_count, plural_suffix
                     );
                     this.child(
-                        Button::new("diagnostics-show-warning-label", label).on_click(cx.listener(
-                            |this, _, window, cx| {
+                        Button::new("diagnostics-show-warning-label", label, cx).on_click(
+                            cx.listener(|this, _, window, cx| {
                                 this.toggle_warnings(&Default::default(), window, cx);
                                 cx.notify();
-                            },
-                        )),
+                            }),
+                        ),
                     )
                 })
         } else {

crates/diagnostics/src/items.rs šŸ”—

@@ -74,7 +74,7 @@ impl Render for DiagnosticIndicator {
         let status = if let Some(diagnostic) = &self.current_diagnostic {
             let message = diagnostic.message.split('\n').next().unwrap().to_string();
             Some(
-                Button::new("diagnostic_message", message)
+                Button::new("diagnostic_message", message, cx)
                     .label_size(LabelSize::Small)
                     .tooltip(|window, cx| {
                         Tooltip::for_action(

crates/editor/src/editor.rs šŸ”—

@@ -23754,10 +23754,10 @@ impl Render for MissingEditPredictionKeybindingTooltip {
                         .gap_1()
                         .items_end()
                         .w_full()
-                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
+                        .child(Button::new("see-key-binding", "See Keybinding", cx).size(ButtonSize::Compact).on_click(|_ev, window, cx| {
                             window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
                         }))
-                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
+                        .child(Button::new("learn-more", "Learn More", cx).size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
                             cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
                         })),
                 )
@@ -23804,7 +23804,7 @@ fn render_diff_hunk_controls(
         .block_mouse_except_scroll()
         .shadow_md()
         .child(if status.has_secondary_hunk() {
-            Button::new(("stage", row as u64), "Stage")
+            Button::new(("stage", row as u64), "Stage", cx)
                 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
                 .tooltip({
                     let focus_handle = editor.focus_handle(cx);
@@ -23831,7 +23831,7 @@ fn render_diff_hunk_controls(
                     }
                 })
         } else {
-            Button::new(("unstage", row as u64), "Unstage")
+            Button::new(("unstage", row as u64), "Unstage", cx)
                 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
                 .tooltip({
                     let focus_handle = editor.focus_handle(cx);
@@ -23859,7 +23859,7 @@ fn render_diff_hunk_controls(
                 })
         })
         .child(
-            Button::new(("restore", row as u64), "Restore")
+            Button::new(("restore", row as u64), "Restore", cx)
                 .tooltip({
                     let focus_handle = editor.focus_handle(cx);
                     move |window, cx| {

crates/extensions_ui/src/components/feature_upsell.rs šŸ”—

@@ -56,7 +56,7 @@ impl RenderOnce for FeatureUpsell {
                 self.docs_url,
                 |el, docs_url| {
                     el.child(
-                        Button::new("open_docs", "View Documentation")
+                        Button::new("open_docs", "View Documentation", cx)
                             .icon(IconName::ArrowUpRight)
                             .icon_size(IconSize::Small)
                             .icon_position(IconPosition::End)

crates/extensions_ui/src/extensions_ui.rs šŸ”—

@@ -596,6 +596,7 @@ impl ExtensionsPage {
                                 Button::new(
                                     SharedString::from(format!("rebuild-{}", extension.id)),
                                     "Rebuild",
+                                    cx,
                                 )
                                 .color(Color::Accent)
                                 .disabled(matches!(status, ExtensionStatus::Upgrading))
@@ -609,7 +610,7 @@ impl ExtensionsPage {
                                 }),
                             )
                             .child(
-                                Button::new(SharedString::from(extension.id.clone()), "Uninstall")
+                                Button::new(SharedString::from(extension.id.clone()), "Uninstall", cx)
                                     .color(Color::Accent)
                                     .disabled(matches!(status, ExtensionStatus::Removing))
                                     .on_click({
@@ -626,6 +627,7 @@ impl ExtensionsPage {
                                     Button::new(
                                         SharedString::from(format!("configure-{}", extension.id)),
                                         "Configure",
+                                        cx,
                                     )
                                     .color(Color::Accent)
                                     .disabled(matches!(status, ExtensionStatus::Installing))
@@ -947,6 +949,7 @@ impl ExtensionsPage {
                 install_or_uninstall: Button::new(
                     SharedString::from(extension.id.clone()),
                     "Install",
+                    cx,
                 ),
                 configure: None,
                 upgrade: None,
@@ -963,6 +966,7 @@ impl ExtensionsPage {
                 install_or_uninstall: Button::new(
                     SharedString::from(extension.id.clone()),
                     "Install",
+                    cx,
                 )
                 .on_click({
                     let extension_id = extension.id.clone();
@@ -980,6 +984,7 @@ impl ExtensionsPage {
                 install_or_uninstall: Button::new(
                     SharedString::from(extension.id.clone()),
                     "Install",
+                    cx,
                 )
                 .disabled(true),
                 configure: None,
@@ -989,23 +994,27 @@ impl ExtensionsPage {
                 install_or_uninstall: Button::new(
                     SharedString::from(extension.id.clone()),
                     "Uninstall",
+                    cx,
                 )
                 .disabled(true),
                 configure: is_configurable.then(|| {
                     Button::new(
                         SharedString::from(format!("configure-{}", extension.id)),
                         "Configure",
+                        cx,
                     )
                     .disabled(true)
                 }),
                 upgrade: Some(
-                    Button::new(SharedString::from(extension.id.clone()), "Upgrade").disabled(true),
+                    Button::new(SharedString::from(extension.id.clone()), "Upgrade", cx)
+                        .disabled(true),
                 ),
             },
             ExtensionStatus::Installed(installed_version) => ExtensionCardButtons {
                 install_or_uninstall: Button::new(
                     SharedString::from(extension.id.clone()),
                     "Uninstall",
+                    cx,
                 )
                 .on_click({
                     let extension_id = extension.id.clone();
@@ -1022,6 +1031,7 @@ impl ExtensionsPage {
                     Button::new(
                         SharedString::from(format!("configure-{}", extension.id)),
                         "Configure",
+                        cx,
                     )
                     .on_click({
                         let extension_id = extension.id.clone();
@@ -1047,7 +1057,7 @@ impl ExtensionsPage {
                     None
                 } else {
                     Some(
-                        Button::new(SharedString::from(extension.id.clone()), "Upgrade")
+                        Button::new(SharedString::from(extension.id.clone()), "Upgrade", cx)
                             .when(!is_compatible, |upgrade_button| {
                                 upgrade_button.disabled(true).tooltip({
                                     let version = extension.manifest.version.clone();
@@ -1085,12 +1095,14 @@ impl ExtensionsPage {
                 install_or_uninstall: Button::new(
                     SharedString::from(extension.id.clone()),
                     "Uninstall",
+                    cx,
                 )
                 .disabled(true),
                 configure: is_configurable.then(|| {
                     Button::new(
                         SharedString::from(format!("configure-{}", extension.id)),
                         "Configure",
+                        cx,
                     )
                     .disabled(true)
                 }),
@@ -1394,7 +1406,7 @@ impl Render for ExtensionsPage {
                             .justify_between()
                             .child(Headline::new("Extensions").size(HeadlineSize::XLarge))
                             .child(
-                                Button::new("install-dev-extension", "Install Dev Extension")
+                                Button::new("install-dev-extension", "Install Dev Extension", cx)
                                     .style(ButtonStyle::Filled)
                                     .size(ButtonSize::Large)
                                     .on_click(|_event, window, cx| {
@@ -1470,7 +1482,7 @@ impl Render for ExtensionsPage {
                     .border_color(cx.theme().colors().border_variant)
                     .overflow_x_scroll()
                     .child(
-                        Button::new("filter-all-categories", "All")
+                        Button::new("filter-all-categories", "All", cx)
                             .when(self.provides_filter.is_none(), |button| {
                                 button.style(ButtonStyle::Filled)
                             })
@@ -1493,7 +1505,7 @@ impl Render for ExtensionsPage {
                         let button_id = SharedString::from(format!("filter-category-{}", label));
 
                         Some(
-                            Button::new(button_id, label)
+                            Button::new(button_id, label, cx)
                                 .style(if self.provides_filter == Some(provides) {
                                     ButtonStyle::Filled
                                 } else {

crates/feedback/src/feedback_modal.rs šŸ”—

@@ -68,7 +68,7 @@ impl Render for FeedbackModal {
             )
             .child(Label::new("Thanks for using Zed! To share your experience with us, reach for the channel that's the most appropriate:"))
             .child(
-                Button::new("file-a-bug-report", "File a Bug Report")
+                Button::new("file-a-bug-report", "File a Bug Report", cx)
                     .full_width()
                     .icon(IconName::Debug)
                     .icon_size(IconSize::XSmall)
@@ -79,7 +79,7 @@ impl Render for FeedbackModal {
                     })),
             )
             .child(
-                Button::new("request-a-feature", "Request a Feature")
+                Button::new("request-a-feature", "Request a Feature", cx)
                     .full_width()
                     .icon(IconName::Sparkle)
                     .icon_size(IconSize::XSmall)
@@ -90,7 +90,7 @@ impl Render for FeedbackModal {
                     })),
             )
             .child(
-                Button::new("send-us_an-email", "Send an Email")
+                Button::new("send-us_an-email", "Send an Email", cx)
                     .full_width()
                     .icon(IconName::Envelope)
                     .icon_size(IconSize::XSmall)
@@ -101,7 +101,7 @@ impl Render for FeedbackModal {
                     })),
             )
             .child(
-                Button::new("zed_repository", "GitHub Repository")
+                Button::new("zed_repository", "GitHub Repository", cx)
                     .full_width()
                     .icon(IconName::Github)
                     .icon_size(IconSize::XSmall)

crates/file_finder/src/file_finder.rs šŸ”—

@@ -1779,7 +1779,7 @@ impl PickerDelegate for FileFinderDelegate {
                                 }),
                         )
                         .child(
-                            Button::new("open-selection", "Open")
+                            Button::new("open-selection", "Open", cx)
                                 .key_binding(
                                     KeyBinding::for_action_in(
                                         &menu::Confirm,

crates/git_ui/src/blame_ui.rs šŸ”—

@@ -301,6 +301,7 @@ impl BlameRenderer for GitBlameRenderer {
                                                                 Button::new(
                                                                     "pull-request-button",
                                                                     format!("#{}", pr.number),
+                                                                    cx,
                                                                 )
                                                                 .color(Color::Muted)
                                                                 .icon(IconName::PullRequest)
@@ -318,6 +319,7 @@ impl BlameRenderer for GitBlameRenderer {
                                                             Button::new(
                                                                 "commit-sha-button",
                                                                 short_commit_id.clone(),
+                                                                cx,
                                                             )
                                                             .style(ButtonStyle::Subtle)
                                                             .color(Color::Muted)

crates/git_ui/src/commit_modal.rs šŸ”—

@@ -363,7 +363,7 @@ impl CommitModal {
             .map(|b| b.name().to_owned())
             .unwrap_or_else(|| "<no branch>".to_owned());
 
-        let branch_picker_button = panel_button(branch)
+        let branch_picker_button = panel_button(branch, cx)
             .icon(IconName::GitBranch)
             .icon_size(IconSize::Small)
             .icon_color(Color::Placeholder)

crates/git_ui/src/commit_tooltip.rs šŸ”—

@@ -283,6 +283,7 @@ impl Render for CommitTooltip {
                                                 Button::new(
                                                     "pull-request-button",
                                                     format!("#{}", pr.number),
+                                                    cx,
                                                 )
                                                 .color(Color::Muted)
                                                 .icon(IconName::PullRequest)
@@ -300,6 +301,7 @@ impl Render for CommitTooltip {
                                             Button::new(
                                                 "commit-sha-button",
                                                 short_commit_id.clone(),
+                                                cx,
                                             )
                                             .style(ButtonStyle::Subtle)
                                             .color(Color::Muted)

crates/git_ui/src/conflict_view.rs šŸ”—

@@ -394,7 +394,7 @@ fn render_conflict_buttons(
         .gap_1()
         .bg(cx.theme().colors().editor_background)
         .child(
-            Button::new("head", "Use HEAD")
+            Button::new("head", "Use HEAD", cx)
                 .label_size(LabelSize::Small)
                 .on_click({
                     let editor = editor.clone();
@@ -414,7 +414,7 @@ fn render_conflict_buttons(
                 }),
         )
         .child(
-            Button::new("origin", "Use Origin")
+            Button::new("origin", "Use Origin", cx)
                 .label_size(LabelSize::Small)
                 .on_click({
                     let editor = editor.clone();
@@ -434,7 +434,7 @@ fn render_conflict_buttons(
                 }),
         )
         .child(
-            Button::new("both", "Use Both")
+            Button::new("both", "Use Both", cx)
                 .label_size(LabelSize::Small)
                 .on_click({
                     let editor = editor.clone();

crates/git_ui/src/git_panel.rs šŸ”—

@@ -3327,7 +3327,7 @@ impl GitPanel {
                 .px_2()
                 .justify_between()
                 .child(
-                    panel_button(change_string)
+                    panel_button(change_string, cx)
                         .color(Color::Muted)
                         .tooltip(Tooltip::for_action_title_in(
                             "Open Diff",
@@ -3345,7 +3345,7 @@ impl GitPanel {
                         .gap_1()
                         .child(self.render_overflow_menu("overflow_menu"))
                         .child(
-                            panel_filled_button(text)
+                            panel_filled_button(text, cx)
                                 .tooltip(Tooltip::for_action_title_in(
                                     tooltip,
                                     action.as_ref(),
@@ -3604,7 +3604,7 @@ impl GitPanel {
                     ),
             )
             .child(
-                panel_button("Cancel")
+                panel_button("Cancel", cx)
                     .size(ButtonSize::Default)
                     .on_click(cx.listener(|this, _, _, cx| this.set_amend_pending(false, cx))),
             )
@@ -3703,7 +3703,7 @@ impl GitPanel {
                     let worktree_count = self.project.read(cx).visible_worktrees(cx).count();
                     (worktree_count > 0 && self.active_repository.is_none()).then(|| {
                         h_flex().w_full().justify_around().child(
-                            panel_filled_button("Initialize Repository")
+                            panel_filled_button("Initialize Repository", cx)
                                 .tooltip(Tooltip::for_action_title_in(
                                     "git init",
                                     &git::Init,
@@ -4841,7 +4841,7 @@ impl RenderOnce for PanelRepoFooter {
             util::truncate_and_trailoff(branch_name.trim_ascii(), branch_display_len)
         };
 
-        let repo_selector_trigger = Button::new("repo-selector", truncated_repo_name)
+        let repo_selector_trigger = Button::new("repo-selector", truncated_repo_name, cx)
             .style(ButtonStyle::Transparent)
             .size(ButtonSize::None)
             .label_size(LabelSize::Small)
@@ -4862,7 +4862,7 @@ impl RenderOnce for PanelRepoFooter {
             .anchor(Corner::BottomLeft)
             .into_any_element();
 
-        let branch_selector_button = Button::new("branch-selector", truncated_branch_name)
+        let branch_selector_button = Button::new("branch-selector", truncated_branch_name, cx)
             .style(ButtonStyle::Transparent)
             .size(ButtonSize::None)
             .label_size(LabelSize::Small)

crates/git_ui/src/onboarding.rs šŸ”—

@@ -118,13 +118,13 @@ impl Render for GitOnboardingModal {
                 )),
             ));
 
-        let open_panel_button = Button::new("open-panel", "Get Started with the Git Panel")
+        let open_panel_button = Button::new("open-panel", "Get Started with the Git Panel", cx)
             .icon_size(IconSize::Indicator)
             .style(ButtonStyle::Tinted(TintColor::Accent))
             .full_width()
             .on_click(cx.listener(Self::open_panel));
 
-        let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
+        let blog_post_button = Button::new("view-blog", "Check out the Blog Post", cx)
             .icon(IconName::ArrowUpRight)
             .icon_size(IconSize::Indicator)
             .icon_color(Color::Muted)

crates/git_ui/src/project_diff.rs šŸ”—

@@ -759,7 +759,7 @@ impl Render for ProjectDiff {
                         })
                         .child(
                             h_flex().justify_around().mt_1().child(
-                                Button::new("project-diff-close-button", "Close")
+                                Button::new("project-diff-close-button", "Close", cx)
                                     // .style(ButtonStyle::Transparent)
                                     .key_binding(KeyBinding::for_action_in(
                                         &CloseActiveItem::default(),
@@ -936,7 +936,7 @@ impl Render for ProjectDiffToolbar {
                 h_group_sm()
                     .when(button_states.selection, |el| {
                         el.child(
-                            Button::new("stage", "Toggle Staged")
+                            Button::new("stage", "Toggle Staged", cx)
                                 .tooltip(Tooltip::for_action_title_in(
                                     "Toggle Staged",
                                     &ToggleStaged,
@@ -950,7 +950,7 @@ impl Render for ProjectDiffToolbar {
                     })
                     .when(!button_states.selection, |el| {
                         el.child(
-                            Button::new("stage", "Stage")
+                            Button::new("stage", "Stage", cx)
                                 .tooltip(Tooltip::for_action_title_in(
                                     "Stage and go to next hunk",
                                     &StageAndNext,
@@ -961,7 +961,7 @@ impl Render for ProjectDiffToolbar {
                                 })),
                         )
                         .child(
-                            Button::new("unstage", "Unstage")
+                            Button::new("unstage", "Unstage", cx)
                                 .tooltip(Tooltip::for_action_title_in(
                                     "Unstage and go to next hunk",
                                     &UnstageAndNext,
@@ -1011,7 +1011,7 @@ impl Render for ProjectDiffToolbar {
                         button_states.unstage_all && !button_states.stage_all,
                         |el| {
                             el.child(
-                                Button::new("unstage-all", "Unstage All")
+                                Button::new("unstage-all", "Unstage All", cx)
                                     .tooltip(Tooltip::for_action_title_in(
                                         "Unstage all changes",
                                         &UnstageAll,
@@ -1030,7 +1030,7 @@ impl Render for ProjectDiffToolbar {
                                 // todo make it so that changing to say "Unstaged"
                                 // doesn't change the position.
                                 div().child(
-                                    Button::new("stage-all", "Stage All")
+                                    Button::new("stage-all", "Stage All", cx)
                                         .disabled(!button_states.stage_all)
                                         .tooltip(Tooltip::for_action_title_in(
                                             "Stage all changes",
@@ -1045,7 +1045,7 @@ impl Render for ProjectDiffToolbar {
                         },
                     )
                     .child(
-                        Button::new("commit", "Commit")
+                        Button::new("commit", "Commit", cx)
                             .tooltip(Tooltip::for_action_title_in(
                                 "Commit",
                                 &Commit,

crates/go_to_line/src/cursor_position.rs šŸ”—

@@ -219,7 +219,7 @@ impl Render for CursorPosition {
             let context = self.context.clone();
 
             el.child(
-                Button::new("go-to-line-column", text)
+                Button::new("go-to-line-column", text, cx)
                     .label_size(LabelSize::Small)
                     .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {

crates/image_viewer/src/image_info.rs šŸ”—

@@ -78,7 +78,7 @@ impl Render for ImageInfo {
         );
 
         div().child(
-            Button::new("image-metadata", components.join(" • ")).label_size(LabelSize::Small),
+            Button::new("image-metadata", components.join(" • "), cx).label_size(LabelSize::Small),
         )
     }
 }

crates/language_models/src/provider/anthropic.rs šŸ”—

@@ -1017,13 +1017,14 @@ impl Render for ConfigurationView {
                     List::new()
                         .child(
                             InstructionListItem::new(
+                                cx,
                                 "Create one by visiting",
                                 Some("Anthropic's settings"),
                                 Some("https://console.anthropic.com/settings/keys")
                             )
                         )
                         .child(
-                            InstructionListItem::text_only("Paste your API key below and hit enter to start using the assistant")
+                            InstructionListItem::text_only(cx, "Paste your API key below and hit enter to start using the assistant")
                         )
                 )
                 .child(
@@ -1066,7 +1067,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset Key")
+                    Button::new("reset-key", "Reset Key", cx)
                         .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)

crates/language_models/src/provider/bedrock.rs šŸ”—

@@ -1232,7 +1232,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset Key")
+                    Button::new("reset-key", "Reset Key", cx)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)
@@ -1257,6 +1257,7 @@ impl Render for ConfigurationView {
                 List::new()
                     .child(
                         InstructionListItem::new(
+                            cx,
                             "Grant permissions to the strategy you'll use according to the:",
                             Some("Prerequisites"),
                             Some("https://docs.aws.amazon.com/bedrock/latest/userguide/inference-prereq.html"),
@@ -1264,6 +1265,7 @@ impl Render for ConfigurationView {
                     )
                     .child(
                         InstructionListItem::new(
+                            cx,
                             "Select the models you would like access to:",
                             Some("Bedrock Model Catalog"),
                             Some("https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/modelaccess"),
@@ -1365,19 +1367,23 @@ impl ConfigurationView {
             .child(
                 List::new()
                     .child(InstructionListItem::new(
+                        cx,
                         "Create an IAM user in the AWS console with programmatic access",
                         Some("IAM Console"),
                         Some("https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/users"),
                     ))
                     .child(InstructionListItem::new(
+                        cx,
                         "Attach the necessary Bedrock permissions to this ",
                         Some("user"),
                         Some("https://docs.aws.amazon.com/bedrock/latest/userguide/inference-prereq.html"),
                     ))
                     .child(InstructionListItem::text_only(
+                        cx,
                         "Copy the access key ID and secret access key when provided",
                     ))
                     .child(InstructionListItem::text_only(
+                        cx,
                         "Enter these credentials below",
                     )),
             )

crates/language_models/src/provider/cloud.rs šŸ”—

@@ -410,12 +410,17 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
             return None;
         }
         Some(
-            render_accept_terms(view, state.accept_terms_of_service_task.is_some(), {
-                let state = self.state.clone();
-                move |_window, cx| {
-                    state.update(cx, |state, cx| state.accept_terms_of_service(cx));
-                }
-            })
+            render_accept_terms(
+                view,
+                state.accept_terms_of_service_task.is_some(),
+                {
+                    let state = self.state.clone();
+                    move |_window, cx| {
+                        state.update(cx, |state, cx| state.accept_terms_of_service(cx));
+                    }
+                },
+                cx,
+            )
             .into_any_element(),
         )
     }
@@ -429,11 +434,12 @@ fn render_accept_terms(
     view_kind: LanguageModelProviderTosView,
     accept_terms_of_service_in_progress: bool,
     accept_terms_callback: impl Fn(&mut Window, &mut App) + 'static,
+    app: &App,
 ) -> impl IntoElement {
     let thread_fresh_start = matches!(view_kind, LanguageModelProviderTosView::ThreadFreshStart);
     let thread_empty_state = matches!(view_kind, LanguageModelProviderTosView::ThreadEmptyState);
 
-    let terms_button = Button::new("terms_of_service", "Terms of Service")
+    let terms_button = Button::new("terms_of_service", "Terms of Service", app)
         .style(ButtonStyle::Subtle)
         .icon(IconName::ArrowUpRight)
         .icon_color(Color::Muted)
@@ -442,7 +448,7 @@ fn render_accept_terms(
         .on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
 
     let button_container = h_flex().child(
-        Button::new("accept_terms", "I accept the Terms of Service")
+        Button::new("accept_terms", "I accept the Terms of Service", app)
             .when(!thread_empty_state, |this| {
                 this.full_width()
                     .style(ButtonStyle::Tinted(TintColor::Accent))
@@ -1135,19 +1141,19 @@ impl RenderOnce for ZedAiConfiguration {
         };
 
         let manage_subscription_buttons = if is_pro {
-            Button::new("manage_settings", "Manage Subscription")
+            Button::new("manage_settings", "Manage Subscription", _cx)
                 .full_width()
                 .style(ButtonStyle::Tinted(TintColor::Accent))
                 .on_click(|_, _, cx| cx.open_url(&zed_urls::account_url(cx)))
                 .into_any_element()
         } else if self.plan.is_none() || self.eligible_for_trial {
-            Button::new("start_trial", "Start 14-day Free Pro Trial")
+            Button::new("start_trial", "Start 14-day Free Pro Trial", _cx)
                 .full_width()
                 .style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
                 .on_click(|_, _, cx| cx.open_url(&zed_urls::start_trial_url(cx)))
                 .into_any_element()
         } else {
-            Button::new("upgrade", "Upgrade to Pro")
+            Button::new("upgrade", "Upgrade to Pro", _cx)
                 .full_width()
                 .style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
                 .on_click(|_, _, cx| cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx)))
@@ -1159,7 +1165,7 @@ impl RenderOnce for ZedAiConfiguration {
                 .gap_2()
                 .child(Label::new("Sign in to have access to Zed's complete agentic experience with hosted models."))
                 .child(
-                    Button::new("sign_in", "Sign In to use Zed AI")
+                    Button::new("sign_in", "Sign In to use Zed AI", _cx)
                         .icon_color(Color::Muted)
                         .icon(IconName::Github)
                         .icon_size(IconSize::Small)
@@ -1183,12 +1189,13 @@ impl RenderOnce for ZedAiConfiguration {
                         let callback = self.accept_terms_of_service_callback.clone();
                         move |window, cx| (callback)(window, cx)
                     },
+                    _cx,
                 ))
             })
             .map(|this| {
                 if self.has_accepted_terms_of_service && self.account_too_young {
                     this.child(young_account_banner).child(
-                        Button::new("upgrade", "Upgrade to Pro")
+                        Button::new("upgrade", "Upgrade to Pro", _cx)
                             .style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
                             .full_width()
                             .on_click(|_, _, cx| {

crates/language_models/src/provider/copilot_chat.rs šŸ”—

@@ -670,7 +670,7 @@ impl Render for ConfigurationView {
                         .child(Label::new("Authorized")),
                 )
                 .child(
-                    Button::new("sign_out", "Sign Out")
+                    Button::new("sign_out", "Sign Out", cx)
                         .label_size(LabelSize::Small)
                         .on_click(|_, window, cx| {
                             window.dispatch_action(copilot::SignOut.boxed_clone(), cx);
@@ -709,7 +709,7 @@ impl Render for ConfigurationView {
                         const LABEL: &str = "To use Zed's agent with GitHub Copilot, you need to be logged in to GitHub. Note that your GitHub account must have an active Copilot Chat subscription.";
 
                         v_flex().gap_2().child(Label::new(LABEL)).child(
-                            Button::new("sign_in", "Sign in to use GitHub Copilot")
+                            Button::new("sign_in", "Sign in to use GitHub Copilot", cx)
                                 .icon_color(Color::Muted)
                                 .icon(IconName::Github)
                                 .icon_position(IconPosition::Start)

crates/language_models/src/provider/deepseek.rs šŸ”—

@@ -679,11 +679,13 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Get your API key from the",
                             Some("DeepSeek console"),
                             Some("https://platform.deepseek.com/api_keys"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the assistant",
                         )),
                 )
@@ -728,7 +730,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset Key")
+                    Button::new("reset-key", "Reset Key", cx)
                         .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)

crates/language_models/src/provider/google.rs šŸ”—

@@ -884,11 +884,13 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Create one by visiting",
                             Some("Google AI's console"),
                             Some("https://aistudio.google.com/app/apikey"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the assistant",
                         )),
                 )
@@ -931,7 +933,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset Key")
+                    Button::new("reset-key", "Reset Key", cx)
                         .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)

crates/language_models/src/provider/lmstudio.rs šŸ”—

@@ -668,9 +668,11 @@ impl Render for ConfigurationView {
                     v_flex().gap_1().child(Label::new(lmstudio_intro)).child(
                         List::new()
                             .child(InstructionListItem::text_only(
+                                cx,
                                 "LM Studio needs to be running with at least one model downloaded.",
                             ))
                             .child(InstructionListItem::text_only(
+                                cx,
                                 "To get your first model, try running `lms get qwen2.5-coder-7b`",
                             )),
                     ),
@@ -687,7 +689,7 @@ impl Render for ConfigurationView {
                                 .map(|this| {
                                     if is_authenticated {
                                         this.child(
-                                            Button::new("lmstudio-site", "LM Studio")
+                                            Button::new("lmstudio-site", "LM Studio", cx)
                                                 .style(ButtonStyle::Subtle)
                                                 .icon(IconName::ArrowUpRight)
                                                 .icon_size(IconSize::Small)
@@ -702,6 +704,7 @@ impl Render for ConfigurationView {
                                             Button::new(
                                                 "download_lmstudio_button",
                                                 "Download LM Studio",
+                                                cx,
                                             )
                                             .style(ButtonStyle::Subtle)
                                             .icon(IconName::ArrowUpRight)
@@ -715,7 +718,7 @@ impl Render for ConfigurationView {
                                     }
                                 })
                                 .child(
-                                    Button::new("view-models", "Model Catalog")
+                                    Button::new("view-models", "Model Catalog", cx)
                                         .style(ButtonStyle::Subtle)
                                         .icon(IconName::ArrowUpRight)
                                         .icon_size(IconSize::Small)
@@ -741,7 +744,7 @@ impl Render for ConfigurationView {
                                 )
                             } else {
                                 this.child(
-                                    Button::new("retry_lmstudio_models", "Connect")
+                                    Button::new("retry_lmstudio_models", "Connect", cx)
                                         .icon_position(IconPosition::Start)
                                         .icon_size(IconSize::XSmall)
                                         .icon(IconName::PlayFilled)

crates/language_models/src/provider/mistral.rs šŸ”—

@@ -848,14 +848,17 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Create one by visiting",
                             Some("Mistral's console"),
                             Some("https://console.mistral.ai/api-keys"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Ensure your Mistral account has credits",
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the assistant",
                         )),
                 )
@@ -898,7 +901,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset Key")
+                    Button::new("reset-key", "Reset Key", cx)
                         .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)

crates/language_models/src/provider/ollama.rs šŸ”—

@@ -587,8 +587,9 @@ impl Render for ConfigurationView {
                 .child(
                     v_flex().gap_1().child(Label::new(ollama_intro)).child(
                         List::new()
-                            .child(InstructionListItem::text_only("Ollama must be running with at least one model installed to use it in the assistant."))
+                            .child(InstructionListItem::text_only(cx, "Ollama must be running with at least one model installed to use it in the assistant."))
                             .child(InstructionListItem::text_only(
+                                cx,
                                 "Once installed, try `ollama run llama3.2`",
                             )),
                     ),
@@ -605,7 +606,7 @@ impl Render for ConfigurationView {
                                 .map(|this| {
                                     if is_authenticated {
                                         this.child(
-                                            Button::new("ollama-site", "Ollama")
+                                            Button::new("ollama-site", "Ollama", cx)
                                                 .style(ButtonStyle::Subtle)
                                                 .icon(IconName::ArrowUpRight)
                                                 .icon_size(IconSize::Small)
@@ -618,6 +619,7 @@ impl Render for ConfigurationView {
                                             Button::new(
                                                 "download_ollama_button",
                                                 "Download Ollama",
+                                                cx,
                                             )
                                             .style(ButtonStyle::Subtle)
                                             .icon(IconName::ArrowUpRight)
@@ -631,7 +633,7 @@ impl Render for ConfigurationView {
                                     }
                                 })
                                 .child(
-                                    Button::new("view-models", "View All Models")
+                                    Button::new("view-models", "View All Models", cx)
                                         .style(ButtonStyle::Subtle)
                                         .icon(IconName::ArrowUpRight)
                                         .icon_size(IconSize::Small)
@@ -655,7 +657,7 @@ impl Render for ConfigurationView {
                                 )
                             } else {
                                 this.child(
-                                    Button::new("retry_ollama_models", "Connect")
+                                    Button::new("retry_ollama_models", "Connect", cx)
                                         .icon_position(IconPosition::Start)
                                         .icon_size(IconSize::XSmall)
                                         .icon(IconName::PlayFilled)

crates/language_models/src/provider/open_ai.rs šŸ”—

@@ -812,14 +812,17 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Create one by visiting",
                             Some("OpenAI's console"),
                             Some("https://platform.openai.com/api-keys"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Ensure your OpenAI account has credits",
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the assistant",
                         )),
                 )
@@ -857,7 +860,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-api-key", "Reset API Key")
+                    Button::new("reset-api-key", "Reset API Key",cx)
                         .label_size(LabelSize::Small)
                         .icon(IconName::Undo)
                         .icon_size(IconSize::Small)
@@ -891,7 +894,7 @@ impl Render for ConfigurationView {
                     .child(Label::new("Zed also supports OpenAI-compatible models.")),
             )
             .child(
-                Button::new("docs", "Learn More")
+                Button::new("docs", "Learn More", cx)
                     .icon(IconName::ArrowUpRight)
                     .icon_size(IconSize::Small)
                     .icon_color(Color::Muted)

crates/language_models/src/provider/open_router.rs šŸ”—

@@ -859,14 +859,17 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Create an API key by visiting",
                             Some("OpenRouter's console"),
                             Some("https://openrouter.ai/keys"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Ensure your OpenRouter account has credits",
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the assistant",
                         )),
                 )
@@ -909,7 +912,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset Key")
+                    Button::new("reset-key", "Reset Key",cx)
                         .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)

crates/language_models/src/provider/vercel.rs šŸ”—

@@ -517,11 +517,13 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Create one by visiting",
                             Some("Vercel v0's console"),
                             Some("https://v0.dev/chat/settings/keys"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the agent",
                         )),
                 )
@@ -559,7 +561,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-api-key", "Reset API Key")
+                    Button::new("reset-api-key", "Reset API Key",cx)
                         .label_size(LabelSize::Small)
                         .icon(IconName::Undo)
                         .icon_size(IconSize::Small)

crates/language_models/src/provider/x_ai.rs šŸ”—

@@ -507,11 +507,13 @@ impl Render for ConfigurationView {
                 .child(
                     List::new()
                         .child(InstructionListItem::new(
+                            cx,
                             "Create one by visiting",
                             Some("xAI console"),
                             Some("https://console.x.ai/team/default/api-keys"),
                         ))
                         .child(InstructionListItem::text_only(
+                            cx,
                             "Paste your API key below and hit enter to start using the agent",
                         )),
                 )
@@ -549,7 +551,7 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-api-key", "Reset API Key")
+                    Button::new("reset-api-key", "Reset API Key",cx)
                         .label_size(LabelSize::Small)
                         .icon(IconName::Undo)
                         .icon_size(IconSize::Small)

crates/language_models/src/ui/instruction_list_item.rs šŸ”—

@@ -2,27 +2,31 @@ use gpui::{AnyElement, IntoElement, ParentElement, SharedString};
 use ui::{ListItem, prelude::*};
 
 /// A reusable list item component for adding LLM provider configuration instructions
-pub struct InstructionListItem {
+pub struct InstructionListItem<'app> {
+    app: &'app App,
     label: SharedString,
     button_label: Option<SharedString>,
     button_link: Option<String>,
 }
 
-impl InstructionListItem {
+impl<'app> InstructionListItem<'app> {
     pub fn new(
+        app: &'app App,
         label: impl Into<SharedString>,
         button_label: Option<impl Into<SharedString>>,
         button_link: Option<impl Into<String>>,
     ) -> Self {
         Self {
+            app,
             label: label.into(),
             button_label: button_label.map(|l| l.into()),
             button_link: button_link.map(|l| l.into()),
         }
     }
 
-    pub fn text_only(label: impl Into<SharedString>) -> Self {
+    pub fn text_only(app: &'app App, label: impl Into<SharedString>) -> Self {
         Self {
+            app,
             label: label.into(),
             button_label: None,
             button_link: None,
@@ -30,7 +34,7 @@ impl InstructionListItem {
     }
 }
 
-impl IntoElement for InstructionListItem {
+impl IntoElement for InstructionListItem<'_> {
     type Element = AnyElement;
 
     fn into_element(self) -> Self::Element {
@@ -44,7 +48,7 @@ impl IntoElement for InstructionListItem {
                 .flex_wrap()
                 .child(Label::new(self.label))
                 .child(
-                    Button::new(unique_id, button_label)
+                    Button::new(unique_id, button_label, self.app)
                         .style(ButtonStyle::Subtle)
                         .icon(IconName::ArrowUpRight)
                         .icon_size(IconSize::Small)

crates/language_selector/src/active_buffer_language.rs šŸ”—

@@ -55,7 +55,7 @@ impl Render for ActiveBufferLanguage {
             };
 
             el.child(
-                Button::new("change-language", active_language_text)
+                Button::new("change-language", active_language_text, cx)
                     .label_size(LabelSize::Small)
                     .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {

crates/language_tools/src/key_context_view.rs šŸ”—

@@ -206,12 +206,12 @@ impl Render for KeyContextView {
                     .mt_4()
                     .gap_4()
                     .child(
-                        Button::new("open_documentation", "Open Documentation")
+                        Button::new("open_documentation", "Open Documentation", cx)
                             .style(ButtonStyle::Filled)
                             .on_click(|_, _, cx| cx.open_url("https://zed.dev/docs/key-bindings")),
                     )
                     .child(
-                        Button::new("view_default_keymap", "View default keymap")
+                        Button::new("view_default_keymap", "View default keymap", cx)
                             .style(ButtonStyle::Filled)
                             .key_binding(ui::KeyBinding::for_action(
                                 &zed_actions::OpenDefaultKeymap,
@@ -223,7 +223,7 @@ impl Render for KeyContextView {
                             }),
                     )
                     .child(
-                        Button::new("edit_your_keymap", "Edit your keymap")
+                        Button::new("edit_your_keymap", "Edit your keymap", cx)
                             .style(ButtonStyle::Filled)
                             .key_binding(ui::KeyBinding::for_action(&zed_actions::OpenKeymap, window, cx))
                             .on_click(|_, window, cx| {

crates/language_tools/src/lsp_log.rs šŸ”—

@@ -1374,6 +1374,7 @@ impl Render for LspLogToolbarItemView {
                             ))
                         })
                         .unwrap_or_else(|| "No server selected".into()),
+                    cx,
                 )
                 .icon(IconName::ChevronDown)
                 .icon_size(IconSize::Small)
@@ -1431,10 +1432,14 @@ impl Render for LspLogToolbarItemView {
             PopoverMenu::new("LspViewSelector")
                 .anchor(Corner::TopLeft)
                 .trigger(
-                    Button::new("language_server_menu_header", server.selected_entry.label())
-                        .icon(IconName::ChevronDown)
-                        .icon_size(IconSize::Small)
-                        .icon_color(Color::Muted),
+                    Button::new(
+                        "language_server_menu_header",
+                        server.selected_entry.label(),
+                        cx,
+                    )
+                    .icon(IconName::ChevronDown)
+                    .icon_size(IconSize::Small)
+                    .icon_color(Color::Muted),
                 )
                 .menu(move |window, cx| {
                     let log_toolbar_view = log_toolbar_view.clone();
@@ -1516,22 +1521,26 @@ impl Render for LspLogToolbarItemView {
                     .gap_0p5()
                     .child(lsp_menu)
                     .children(view_selector)
-                    .child(
+                    .child({
+                        let trace_button =
+                            Button::new("language_server_trace_level_selector", "Trace level", cx)
+                                .icon(IconName::ChevronDown)
+                                .icon_size(IconSize::Small)
+                                .icon_color(Color::Muted);
+
+                        let log_button =
+                            Button::new("language_server_log_level_selector", "Log level", cx)
+                                .icon(IconName::ChevronDown)
+                                .icon_size(IconSize::Small)
+                                .icon_color(Color::Muted);
+
                         log_view.update(cx, |this, _cx| match this.active_entry_kind {
                             LogKind::Trace => {
                                 let log_view = log_view.clone();
                                 div().child(
                                     PopoverMenu::new("lsp-trace-level-menu")
                                         .anchor(Corner::TopLeft)
-                                        .trigger(
-                                            Button::new(
-                                                "language_server_trace_level_selector",
-                                                "Trace level",
-                                            )
-                                            .icon(IconName::ChevronDown)
-                                            .icon_size(IconSize::Small)
-                                            .icon_color(Color::Muted),
-                                        )
+                                        .trigger(trace_button)
                                         .menu({
                                             let log_view = log_view.clone();
 
@@ -1591,15 +1600,7 @@ impl Render for LspLogToolbarItemView {
                                 div().child(
                                     PopoverMenu::new("lsp-log-level-menu")
                                         .anchor(Corner::TopLeft)
-                                        .trigger(
-                                            Button::new(
-                                                "language_server_log_level_selector",
-                                                "Log level",
-                                            )
-                                            .icon(IconName::ChevronDown)
-                                            .icon_size(IconSize::Small)
-                                            .icon_color(Color::Muted),
-                                        )
+                                        .trigger(log_button)
                                         .menu({
                                             let log_view = log_view.clone();
 
@@ -1656,11 +1657,11 @@ impl Render for LspLogToolbarItemView {
                                 )
                             }
                             _ => div(),
-                        }),
-                    ),
+                        })
+                    }),
             )
             .child(
-                Button::new("clear_log_button", "Clear").on_click(cx.listener(
+                Button::new("clear_log_button", "Clear", cx).on_click(cx.listener(
                     |this, _, window, cx| {
                         if let Some(log_view) = this.log_view.as_ref() {
                             log_view.update(cx, |log_view, cx| {

crates/notifications/src/status_toast.rs šŸ”—

@@ -122,7 +122,7 @@ impl Render for StatusToast {
             .child(Label::new(self.text.clone()).color(Color::Default))
             .when_some(self.action.as_ref(), |this, action| {
                 this.child(
-                    Button::new(action.id.clone(), action.label.clone())
+                    Button::new(action.id.clone(), action.label.clone(), cx)
                         .tooltip(Tooltip::for_action_title(
                             action.label.clone(),
                             &toast::RunAction,

crates/onboarding/src/ai_setup_page.rs šŸ”—

@@ -80,7 +80,7 @@ fn render_privacy_card(tab_index: &mut isize, disabled: bool, cx: &mut App) -> i
                                 .tooltip(move |_, cx| cx.new(|_| AiPrivacyTooltip::new()).into()),
                         )
                         .child(
-                            Button::new("learn_more", "Learn More")
+                            Button::new("learn_more", "Learn More", cx)
                                 .style(ButtonStyle::Outlined)
                                 .label_size(LabelSize::Small)
                                 .icon(IconName::ArrowUpRight)
@@ -206,7 +206,7 @@ fn render_llm_provider_card(
         ))
         .child(Divider::horizontal())
         .child(
-            Button::new("agent_settings", "Add Many Others")
+            Button::new("agent_settings", "Add Many Others", cx)
                 .size(ButtonSize::Large)
                 .icon(IconName::Plus)
                 .icon_position(IconPosition::Start)
@@ -363,7 +363,7 @@ impl Render for AiConfigurationModal {
                     .section(Section::new().child(self.configuration_view.clone()))
                     .footer(
                         ModalFooter::new().end_slot(
-                            Button::new("ai-onb-modal-Done", "Done")
+                            Button::new("ai-onb-modal-Done", "Done", cx)
                                 .key_binding(
                                     KeyBinding::for_action_in(
                                         &menu::Cancel,

crates/onboarding/src/onboarding.rs šŸ”—

@@ -458,7 +458,7 @@ impl Onboarding {
                         )
                         .into_any_element()
                 } else {
-                    Button::new("sign_in", "Sign In")
+                    Button::new("sign_in", "Sign In", cx)
                         .full_width()
                         .style(ButtonStyle::Outlined)
                         .size(ButtonSize::Medium)

crates/onboarding/src/welcome.rs šŸ”—

@@ -235,7 +235,7 @@ impl Render for WelcomePage {
                                             .border_color(cx.theme().colors().border.opacity(0.6))
                                             .border_dashed()
                                             .child(
-                                                    Button::new("welcome-exit", "Return to Setup")
+                                                    Button::new("welcome-exit", "Return to Setup", cx)
                                                         .tab_index(last_index as isize)
                                                         .full_width()
                                                         .label_size(LabelSize::XSmall)

crates/panel/src/panel.rs šŸ”—

@@ -50,10 +50,10 @@ impl RenderOnce for PanelTab {
     }
 }
 
-pub fn panel_button(label: impl Into<SharedString>) -> ui::Button {
+pub fn panel_button(label: impl Into<SharedString>, app: &App) -> ui::Button {
     let label = label.into();
     let id = ElementId::Name(label.clone().to_lowercase().replace(' ', "_").into());
-    ui::Button::new(id, label)
+    ui::Button::new(id, label, app)
         .label_size(ui::LabelSize::Small)
         .icon_size(ui::IconSize::Small)
         // TODO: Change this once we use on_surface_bg in button_like
@@ -61,8 +61,8 @@ pub fn panel_button(label: impl Into<SharedString>) -> ui::Button {
         .size(ui::ButtonSize::Compact)
 }
 
-pub fn panel_filled_button(label: impl Into<SharedString>) -> ui::Button {
-    panel_button(label).style(ui::ButtonStyle::Filled)
+pub fn panel_filled_button(label: impl Into<SharedString>, app: &App) -> ui::Button {
+    panel_button(label, app).style(ui::ButtonStyle::Filled)
 }
 
 pub fn panel_icon_button(id: impl Into<SharedString>, icon: IconName) -> ui::IconButton {

crates/project_panel/src/project_panel.rs šŸ”—

@@ -5531,7 +5531,7 @@ impl Render for ProjectPanel {
                 .p_4()
                 .track_focus(&self.focus_handle(cx))
                 .child(
-                    Button::new("open_project", "Open a project")
+                    Button::new("open_project", "Open a project", cx)
                         .full_width()
                         .key_binding(KeyBinding::for_action_in(
                             &OpenRecent::default(),

crates/recent_projects/src/disconnected_overlay.rs šŸ”—

@@ -185,7 +185,7 @@ impl Render for DisconnectedOverlay {
                             h_flex()
                                 .gap_2()
                                 .child(
-                                    Button::new("close-window", "Close Window")
+                                    Button::new("close-window", "Close Window", cx)
                                         .style(ButtonStyle::Filled)
                                         .layer(ElevationIndex::ModalSurface)
                                         .on_click(cx.listener(move |_, _, window, _| {
@@ -194,7 +194,7 @@ impl Render for DisconnectedOverlay {
                                 )
                                 .when(can_reconnect, |el| {
                                     el.child(
-                                        Button::new("reconnect", "Reconnect")
+                                        Button::new("reconnect", "Reconnect", cx)
                                             .style(ButtonStyle::Filled)
                                             .layer(ElevationIndex::ModalSurface)
                                             .icon(IconName::ArrowCircle)

crates/recent_projects/src/recent_projects.rs šŸ”—

@@ -479,7 +479,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                 .border_t_1()
                 .border_color(cx.theme().colors().border_variant)
                 .child(
-                    Button::new("remote", "Open Remote Folder")
+                    Button::new("remote", "Open Remote Folder", cx)
                         .key_binding(KeyBinding::for_action(
                             &OpenRemote {
                                 from_existing_connection: false,
@@ -500,7 +500,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                         }),
                 )
                 .child(
-                    Button::new("local", "Open Local Folder")
+                    Button::new("local", "Open Local Folder", cx)
                         .key_binding(KeyBinding::for_action(&workspace::Open, window, cx))
                         .on_click(|_, window, cx| {
                             window.dispatch_action(workspace::Open.boxed_clone(), cx)

crates/recent_projects/src/remote_servers.rs šŸ”—

@@ -1094,7 +1094,7 @@ impl RemoteServerProjects {
                                         .size(LabelSize::Small),
                                     )
                                     .child(
-                                        Button::new("learn-more", "Learn more…")
+                                        Button::new("learn-more", "Learn more…", cx)
                                             .label_size(LabelSize::Small)
                                             .size(ButtonSize::None)
                                             .color(Color::Accent)

crates/repl/src/components/kernel_options.rs šŸ”—

@@ -234,7 +234,7 @@ impl PickerDelegate for KernelPickerDelegate {
                 .p_1()
                 .gap_4()
                 .child(
-                    Button::new("kernel-docs", "Kernel Docs")
+                    Button::new("kernel-docs", "Kernel Docs", cx)
                         .icon(IconName::ArrowUpRight)
                         .icon_size(IconSize::Small)
                         .icon_color(Color::Muted)

crates/repl/src/session.rs šŸ”—

@@ -655,7 +655,7 @@ impl Render for Session {
                     .as_ref()
                     .map(|info| info.language_info.name.clone()),
                 Some(
-                    Button::new("interrupt", "Interrupt")
+                    Button::new("interrupt", "Interrupt", cx)
                         .style(ButtonStyle::Subtle)
                         .on_click(cx.listener(move |session, _, _, cx| {
                             session.interrupt(cx);
@@ -684,7 +684,7 @@ impl Render for Session {
             .child(Label::new(self.kernel_specification.name()))
             .children(status_text.map(|status_text| Label::new(format!("({status_text})"))))
             .button(
-                Button::new("shutdown", "Shutdown")
+                Button::new("shutdown", "Shutdown", cx)
                     .style(ButtonStyle::Subtle)
                     .disabled(self.kernel.is_shutting_down())
                     .on_click(cx.listener(move |session, _, window, cx| {

crates/rules_library/src/rules_library.rs šŸ”—

@@ -1318,19 +1318,21 @@ impl Render for RulesLibrary {
                                                             "Create your first rule:",
                                                         ))
                                                         .child(
-                                                            Button::new("create-rule", "New Rule")
-                                                                .full_width()
-                                                                .key_binding(
-                                                                    KeyBinding::for_action(
-                                                                        &NewRule, window, cx,
-                                                                    ),
+                                                            Button::new(
+                                                                "create-rule",
+                                                                "New Rule",
+                                                                cx,
+                                                            )
+                                                            .full_width()
+                                                            .key_binding(KeyBinding::for_action(
+                                                                &NewRule, window, cx,
+                                                            ))
+                                                            .on_click(|_, window, cx| {
+                                                                window.dispatch_action(
+                                                                    NewRule.boxed_clone(),
+                                                                    cx,
                                                                 )
-                                                                .on_click(|_, window, cx| {
-                                                                    window.dispatch_action(
-                                                                        NewRule.boxed_clone(),
-                                                                        cx,
-                                                                    )
-                                                                }),
+                                                            }),
                                                         ),
                                                 )
                                                 .child(h_flex()),

crates/search/src/project_search.rs šŸ”—

@@ -1439,7 +1439,7 @@ impl ProjectSearchView {
                     .mb_2(),
             )
             .child(
-                Button::new("filter-paths", "Include/exclude specific paths")
+                Button::new("filter-paths", "Include/exclude specific paths", cx)
                     .icon(IconName::Filter)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
@@ -1454,7 +1454,7 @@ impl ProjectSearchView {
                     }),
             )
             .child(
-                Button::new("find-replace", "Find and replace")
+                Button::new("find-replace", "Find and replace", cx)
                     .icon(IconName::Replace)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
@@ -1469,7 +1469,7 @@ impl ProjectSearchView {
                     }),
             )
             .child(
-                Button::new("regex", "Match with regex")
+                Button::new("regex", "Match with regex", cx)
                     .icon(IconName::Regex)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
@@ -1484,7 +1484,7 @@ impl ProjectSearchView {
                     }),
             )
             .child(
-                Button::new("match-case", "Match case")
+                Button::new("match-case", "Match case", cx)
                     .icon(IconName::CaseSensitive)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)
@@ -1499,7 +1499,7 @@ impl ProjectSearchView {
                     }),
             )
             .child(
-                Button::new("match-whole-words", "Match whole words")
+                Button::new("match-whole-words", "Match whole words", cx)
                     .icon(IconName::WholeWord)
                     .icon_position(IconPosition::Start)
                     .icon_size(IconSize::Small)

crates/semantic_index/src/project_index_debug_view.rs šŸ”—

@@ -135,6 +135,15 @@ impl ProjectIndexDebugView {
 
         let colors = cx.theme().colors();
         let chunk = &state.chunks[ix];
+        let chunk_count = state.chunks.len();
+
+        let prev_button = Button::new(("prev", ix), "prev", cx)
+            .disabled(ix == 0)
+            .on_click(cx.listener(move |this, _, _, _| this.scroll_to_chunk(ix.saturating_sub(1))));
+
+        let next_button = Button::new(("next", ix), "next", cx)
+            .disabled(ix + 1 == chunk_count)
+            .on_click(cx.listener(move |this, _, _, _| this.scroll_to_chunk(ix + 1)));
 
         div()
             .text_ui(cx)
@@ -146,26 +155,10 @@ impl ProjectIndexDebugView {
                     .child(format!(
                         "chunk {} of {}. length: {}",
                         ix + 1,
-                        state.chunks.len(),
+                        chunk_count,
                         chunk.len(),
                     ))
-                    .child(
-                        h_flex()
-                            .child(
-                                Button::new(("prev", ix), "prev")
-                                    .disabled(ix == 0)
-                                    .on_click(cx.listener(move |this, _, _, _| {
-                                        this.scroll_to_chunk(ix.saturating_sub(1))
-                                    })),
-                            )
-                            .child(
-                                Button::new(("next", ix), "next")
-                                    .disabled(ix + 1 == state.chunks.len())
-                                    .on_click(cx.listener(move |this, _, _, _| {
-                                        this.scroll_to_chunk(ix + 1)
-                                    })),
-                            ),
-                    ),
+                    .child(h_flex().child(prev_button).child(next_button)),
             )
             .child(
                 div()

crates/settings_ui/src/keybindings.rs šŸ”—

@@ -2469,7 +2469,7 @@ impl Render for KeybindingEditorModal {
                                                         .color(Color::Muted),
                                                 )
                                                 .child(
-                                                    Button::new("show_matching", "View")
+                                                    Button::new("show_matching", "View", cx)
                                                         .label_size(LabelSize::Small)
                                                         .icon(IconName::ArrowUpRight)
                                                         .icon_color(Color::Muted)
@@ -2508,10 +2508,10 @@ impl Render for KeybindingEditorModal {
                             h_flex()
                                 .gap_1()
                                 .child(
-                                    Button::new("cancel", "Cancel")
+                                    Button::new("cancel", "Cancel", cx)
                                         .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
                                 )
-                                .child(Button::new("save-btn", "Save").on_click(cx.listener(
+                                .child(Button::new("save-btn", "Save", cx).on_click(cx.listener(
                                     |this, _event, _window, cx| {
                                         this.save_or_display_error(cx);
                                     },

crates/settings_ui/src/ui_components/table.rs šŸ”—

@@ -1410,7 +1410,7 @@ impl Component for Table<3> {
                                     "Project A".into_any_element(),
                                     "High".into_any_element(),
                                     "2023-12-31".into_any_element(),
-                                    Button::new("view_a", "View")
+                                    Button::new("view_a", "View", _cx)
                                         .style(ButtonStyle::Filled)
                                         .full_width()
                                         .into_any_element(),
@@ -1420,7 +1420,7 @@ impl Component for Table<3> {
                                     "Project B".into_any_element(),
                                     "Medium".into_any_element(),
                                     "2024-03-15".into_any_element(),
-                                    Button::new("view_b", "View")
+                                    Button::new("view_b", "View", _cx)
                                         .style(ButtonStyle::Filled)
                                         .full_width()
                                         .into_any_element(),
@@ -1430,7 +1430,7 @@ impl Component for Table<3> {
                                     "Project C".into_any_element(),
                                     "Low".into_any_element(),
                                     "2024-06-30".into_any_element(),
-                                    Button::new("view_c", "View")
+                                    Button::new("view_c", "View", _cx)
                                         .style(ButtonStyle::Filled)
                                         .full_width()
                                         .into_any_element(),

crates/tasks_ui/src/modal.rs šŸ”—

@@ -668,7 +668,7 @@ impl PickerDelegate for TasksModalDelegate {
                         .map(|(label, action)| {
                             let keybind = KeyBinding::for_action(&*action, window, cx);
 
-                            Button::new("edit-current-task", label)
+                            Button::new("edit-current-task", label, cx)
                                 .when_some(keybind, |this, keybind| this.key_binding(keybind))
                                 .on_click(move |_, window, cx| {
                                     window.dispatch_action(action.boxed_clone(), cx);
@@ -691,7 +691,7 @@ impl PickerDelegate for TasksModalDelegate {
                                 "Spawn Oneshot"
                             };
 
-                            Button::new("spawn-onehshot", spawn_oneshot_label)
+                            Button::new("spawn-onehshot", spawn_oneshot_label, cx)
                                 .key_binding(keybind)
                                 .on_click(move |_, window, cx| {
                                     window.dispatch_action(action.boxed_clone(), cx)
@@ -706,14 +706,14 @@ impl PickerDelegate for TasksModalDelegate {
                                     } else {
                                         "Spawn Without History"
                                     };
-                                    Button::new("spawn", label).key_binding(keybind).on_click(
-                                        move |_, window, cx| {
+                                    Button::new("spawn", label, cx)
+                                        .key_binding(keybind)
+                                        .on_click(move |_, window, cx| {
                                             window.dispatch_action(
                                                 menu::SecondaryConfirm.boxed_clone(),
                                                 cx,
                                             )
-                                        },
-                                    )
+                                        })
                                 },
                             ),
                         )
@@ -723,7 +723,7 @@ impl PickerDelegate for TasksModalDelegate {
                                 let run_entry_label =
                                     if is_recent_selected { "Rerun" } else { "Spawn" };
 
-                                Button::new("spawn", run_entry_label)
+                                Button::new("spawn", run_entry_label, cx)
                                     .key_binding(keybind)
                                     .on_click(|_, window, cx| {
                                         window.dispatch_action(menu::Confirm.boxed_clone(), cx);

crates/theme_selector/src/icon_theme_selector.rs šŸ”—

@@ -315,7 +315,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
                 .border_t_1()
                 .border_color(cx.theme().colors().border_variant)
                 .child(
-                    Button::new("docs", "View Icon Theme Docs")
+                    Button::new("docs", "View Icon Theme Docs", cx)
                         .icon(IconName::ArrowUpRight)
                         .icon_position(IconPosition::End)
                         .icon_size(IconSize::Small)
@@ -325,7 +325,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
                         }),
                 )
                 .child(
-                    Button::new("more-icon-themes", "Install Icon Themes").on_click(
+                    Button::new("more-icon-themes", "Install Icon Themes", cx).on_click(
                         move |_event, window, cx| {
                             window.dispatch_action(
                                 Box::new(Extensions {

crates/theme_selector/src/theme_selector.rs šŸ”—

@@ -373,7 +373,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
                 .border_t_1()
                 .border_color(cx.theme().colors().border_variant)
                 .child(
-                    Button::new("docs", "View Theme Docs")
+                    Button::new("docs", "View Theme Docs", cx)
                         .icon(IconName::ArrowUpRight)
                         .icon_position(IconPosition::End)
                         .icon_size(IconSize::Small)
@@ -383,7 +383,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
                         })),
                 )
                 .child(
-                    Button::new("more-themes", "Install Themes").on_click(cx.listener({
+                    Button::new("more-themes", "Install Themes", cx).on_click(cx.listener({
                         move |_, _, window, cx| {
                             window.dispatch_action(
                                 Box::new(Extensions {

crates/title_bar/src/application_menu.rs šŸ”—

@@ -163,7 +163,7 @@ impl ApplicationMenu {
             )
     }
 
-    fn render_standard_menu(&self, entry: &MenuEntry) -> impl IntoElement {
+    fn render_standard_menu(&self, entry: &MenuEntry, cx: &App) -> impl IntoElement {
         let current_handle = entry.handle.clone();
 
         let menu_name = entry.menu.name.clone();
@@ -187,6 +187,7 @@ impl ApplicationMenu {
                         Button::new(
                             SharedString::from(format!("{}-menu-trigger", menu_name)),
                             menu_name.clone(),
+                            cx,
                         )
                         .style(ButtonStyle::Subtle)
                         .label_size(LabelSize::Small),
@@ -310,7 +311,7 @@ impl Render for ApplicationMenu {
                 this.children(
                     self.entries
                         .iter()
-                        .map(|entry| self.render_standard_menu(entry)),
+                        .map(|entry| self.render_standard_menu(entry, cx)),
                 )
             })
     }

crates/title_bar/src/collab.rs šŸ”—

@@ -366,6 +366,7 @@ impl TitleBar {
                 Button::new(
                     "toggle_sharing",
                     if is_shared { "Unshare" } else { "Share" },
+                    cx,
                 )
                 .tooltip(Tooltip::text(if is_shared {
                     "Stop sharing project with call participants"

crates/title_bar/src/title_bar.rs šŸ”—

@@ -390,7 +390,7 @@ impl TitleBar {
 
         if self.project.read(cx).is_disconnected(cx) {
             return Some(
-                Button::new("disconnected", "Disconnected")
+                Button::new("disconnected", "Disconnected", cx)
                     .disabled(true)
                     .color(Color::Disabled)
                     .style(ButtonStyle::Subtle)
@@ -407,7 +407,7 @@ impl TitleBar {
             .participant_indices()
             .get(&host_user.id)?;
         Some(
-            Button::new("project_owner_trigger", host_user.github_login.clone())
+            Button::new("project_owner_trigger", host_user.github_login.clone(), cx)
                 .color(Color::Player(participant_index.0))
                 .style(ButtonStyle::Subtle)
                 .label_size(LabelSize::Small)
@@ -445,7 +445,7 @@ impl TitleBar {
             "Open recent project".to_string()
         };
 
-        Button::new("project_name_trigger", name)
+        Button::new("project_name_trigger", name, cx)
             .when(!is_project_selected, |b| b.color(Color::Muted))
             .style(ButtonStyle::Subtle)
             .label_size(LabelSize::Small)
@@ -491,7 +491,7 @@ impl TitleBar {
         }?;
 
         Some(
-            Button::new("project_branch_trigger", branch_name)
+            Button::new("project_branch_trigger", branch_name, cx)
                 .color(Color::Muted)
                 .style(ButtonStyle::Subtle)
                 .label_size(LabelSize::Small)
@@ -590,7 +590,7 @@ impl TitleBar {
                 };
 
                 Some(
-                    Button::new("connection-status", label)
+                    Button::new("connection-status", label, cx)
                         .label_size(LabelSize::Small)
                         .on_click(|_, window, cx| {
                             if let Some(auto_updater) = auto_update::AutoUpdater::get(cx) {
@@ -608,9 +608,9 @@ impl TitleBar {
         }
     }
 
-    pub fn render_sign_in_button(&mut self, _: &mut Context<Self>) -> Button {
+    pub fn render_sign_in_button(&mut self, cx: &mut Context<Self>) -> Button {
         let client = self.client.clone();
-        Button::new("sign_in", "Sign in")
+        Button::new("sign_in", "Sign in", cx)
             .label_size(LabelSize::Small)
             .on_click(move |_, window, cx| {
                 let client = client.clone();

crates/toolchain_selector/src/active_toolchain.rs šŸ”—

@@ -232,7 +232,7 @@ impl Render for ActiveToolchain {
         div().when_some(self.active_toolchain.as_ref(), |el, active_toolchain| {
             let term = self.term.clone();
             el.child(
-                Button::new("change-toolchain", active_toolchain.name.clone())
+                Button::new("change-toolchain", active_toolchain.name.clone(), cx)
                     .label_size(LabelSize::Small)
                     .on_click(cx.listener(|this, _, window, cx| {
                         if let Some(workspace) = this.workspace.upgrade() {

crates/ui/src/components/banner.rs šŸ”—

@@ -134,7 +134,7 @@ impl Component for Banner {
         ComponentScope::DataDisplay
     }
 
-    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         let severity_examples = vec![
             single_example(
                 "Default",
@@ -148,7 +148,7 @@ impl Component for Banner {
                     .severity(Severity::Info)
                     .child(Label::new("This is an informational message"))
                     .action_slot(
-                        Button::new("learn-more", "Learn More")
+                        Button::new("learn-more", "Learn More", cx)
                             .icon(IconName::ArrowUpRight)
                             .icon_size(IconSize::Small)
                             .icon_position(IconPosition::End),
@@ -160,7 +160,7 @@ impl Component for Banner {
                 Banner::new()
                     .severity(Severity::Success)
                     .child(Label::new("Operation completed successfully"))
-                    .action_slot(Button::new("dismiss", "Dismiss"))
+                    .action_slot(Button::new("dismiss", "Dismiss", cx))
                     .into_any_element(),
             ),
             single_example(
@@ -168,7 +168,7 @@ impl Component for Banner {
                 Banner::new()
                     .severity(Severity::Warning)
                     .child(Label::new("Your settings file uses deprecated settings"))
-                    .action_slot(Button::new("update", "Update Settings"))
+                    .action_slot(Button::new("update", "Update Settings", cx))
                     .into_any_element(),
             ),
             single_example(
@@ -176,7 +176,7 @@ impl Component for Banner {
                 Banner::new()
                     .severity(Severity::Error)
                     .child(Label::new("Connection error: unable to connect to server"))
-                    .action_slot(Button::new("reconnect", "Retry"))
+                    .action_slot(Button::new("reconnect", "Retry", cx))
                     .into_any_element(),
             ),
         ];

crates/ui/src/components/button/button.rs šŸ”—

@@ -1,5 +1,5 @@
 use crate::component_prelude::*;
-use gpui::{AnyElement, AnyView, DefiniteLength};
+use gpui::{AnyElement, AnyView, DefiniteLength, FocusHandle, Focusable};
 use ui_macros::RegisterComponent;
 
 use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label};
@@ -79,6 +79,7 @@ use super::button_icon::ButtonIcon;
 ///
 #[derive(IntoElement, Documented, RegisterComponent)]
 pub struct Button {
+    focus_handle: FocusHandle,
     base: ButtonLike,
     label: SharedString,
     label_color: Option<Color>,
@@ -104,8 +105,9 @@ impl Button {
     /// the button with the provided identifier and label text, setting all other
     /// properties to their default values, which can be customized using the
     /// builder pattern methods provided by this struct.
-    pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
+    pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>, app: &App) -> Self {
         Self {
+            focus_handle: app.focus_handle(),
             base: ButtonLike::new(id),
             label: label.into(),
             label_color: None,
@@ -216,6 +218,12 @@ impl Button {
     }
 }
 
+impl Focusable for Button {
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
+        self.focus_handle.clone()
+    }
+}
+
 impl Toggleable for Button {
     /// Sets the selected state of the button.
     ///
@@ -483,7 +491,7 @@ impl Component for Button {
         Some("A button triggers an event or action.")
     }
 
-    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         Some(
             v_flex()
                 .gap_6()
@@ -493,29 +501,29 @@ impl Component for Button {
                         vec![
                             single_example(
                                 "Default",
-                                Button::new("default", "Default").into_any_element(),
+                                Button::new("default", "Default", cx).into_any_element(),
                             ),
                             single_example(
                                 "Filled",
-                                Button::new("filled", "Filled")
+                                Button::new("filled", "Filled", cx)
                                     .style(ButtonStyle::Filled)
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Subtle",
-                                Button::new("outline", "Subtle")
+                                Button::new("outlined", "Outlined", cx)
                                     .style(ButtonStyle::Subtle)
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Tinted",
-                                Button::new("tinted_accent_style", "Accent")
+                                Button::new("tinted_accent_style", "Accent", cx)
                                     .style(ButtonStyle::Tinted(TintColor::Accent))
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Transparent",
-                                Button::new("transparent", "Transparent")
+                                Button::new("transparent", "Transparent", cx)
                                     .style(ButtonStyle::Transparent)
                                     .into_any_element(),
                             ),
@@ -526,25 +534,25 @@ impl Component for Button {
                         vec![
                             single_example(
                                 "Accent",
-                                Button::new("tinted_accent", "Accent")
+                                Button::new("color_accent", "Accent", cx)
                                     .style(ButtonStyle::Tinted(TintColor::Accent))
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Error",
-                                Button::new("tinted_negative", "Error")
+                                Button::new("tinted_negative", "Error", cx)
                                     .style(ButtonStyle::Tinted(TintColor::Error))
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Warning",
-                                Button::new("tinted_warning", "Warning")
+                                Button::new("tinted_warning", "Warning", cx)
                                     .style(ButtonStyle::Tinted(TintColor::Warning))
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Success",
-                                Button::new("tinted_positive", "Success")
+                                Button::new("tinted_positive", "Success", cx)
                                     .style(ButtonStyle::Tinted(TintColor::Success))
                                     .into_any_element(),
                             ),
@@ -555,17 +563,17 @@ impl Component for Button {
                         vec![
                             single_example(
                                 "Default",
-                                Button::new("default_state", "Default").into_any_element(),
+                                Button::new("default_state", "Default", cx).into_any_element(),
                             ),
                             single_example(
                                 "Disabled",
-                                Button::new("disabled", "Disabled")
+                                Button::new("disabled", "Disabled", cx)
                                     .disabled(true)
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Selected",
-                                Button::new("selected", "Selected")
+                                Button::new("selected", "Selected", cx)
                                     .toggle_state(true)
                                     .into_any_element(),
                             ),
@@ -576,21 +584,21 @@ impl Component for Button {
                         vec![
                             single_example(
                                 "Icon Start",
-                                Button::new("icon_start", "Icon Start")
+                                Button::new("icon-start", "Icon Start", cx)
                                     .icon(IconName::Check)
                                     .icon_position(IconPosition::Start)
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Icon End",
-                                Button::new("icon_end", "Icon End")
+                                Button::new("icon-end", "Icon End", cx)
                                     .icon(IconName::Check)
                                     .icon_position(IconPosition::End)
                                     .into_any_element(),
                             ),
                             single_example(
                                 "Icon Color",
-                                Button::new("icon_color", "Icon Color")
+                                Button::new("icon_color", "Icon Color", cx)
                                     .icon(IconName::Check)
                                     .icon_color(Color::Accent)
                                     .into_any_element(),

crates/ui/src/components/callout.rs šŸ”—

@@ -167,7 +167,7 @@ impl Component for Callout {
         )
     }
 
-    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         let callout_examples = vec![
             single_example(
                 "Simple with Title Only",
@@ -178,7 +178,9 @@ impl Component for Callout {
                             .size(IconSize::Small),
                     )
                     .title("System maintenance scheduled for tonight")
-                    .primary_action(Button::new("got-it", "Got it").label_size(LabelSize::Small))
+                    .primary_action(
+                        Button::new("got-it", "Got it", cx).label_size(LabelSize::Small),
+                    )
                     .into_any_element(),
             )
             .width(px(580.)),
@@ -195,10 +197,10 @@ impl Component for Callout {
                         "We'll backup your current settings and update them to the new format.",
                     )
                     .primary_action(
-                        Button::new("update", "Backup & Update").label_size(LabelSize::Small),
+                        Button::new("update", "Backup & Update", cx).label_size(LabelSize::Small),
                     )
                     .secondary_action(
-                        Button::new("dismiss", "Dismiss").label_size(LabelSize::Small),
+                        Button::new("dismiss", "Dismiss", cx).label_size(LabelSize::Small),
                     )
                     .into_any_element(),
             )
@@ -214,10 +216,12 @@ impl Component for Callout {
                     .title("Thread reached the token limit")
                     .description("Start a new thread from a summary to continue the conversation.")
                     .primary_action(
-                        Button::new("new-thread", "Start New Thread").label_size(LabelSize::Small),
+                        Button::new("new-thread", "Start New Thread", cx)
+                            .label_size(LabelSize::Small),
                     )
                     .secondary_action(
-                        Button::new("view-summary", "View Summary").label_size(LabelSize::Small),
+                        Button::new("view-summary", "View Summary", cx)
+                            .label_size(LabelSize::Small),
                     )
                     .into_any_element(),
             )
@@ -233,10 +237,10 @@ impl Component for Callout {
                     .title("Upgrade to Pro")
                     .description("• Unlimited threads\n• Priority support\n• Advanced analytics")
                     .primary_action(
-                        Button::new("upgrade", "Upgrade Now").label_size(LabelSize::Small),
+                        Button::new("upgrade", "Upgrade Now", cx).label_size(LabelSize::Small),
                     )
                     .secondary_action(
-                        Button::new("learn-more", "Learn More").label_size(LabelSize::Small),
+                        Button::new("learn-more", "Learn More", cx).label_size(LabelSize::Small),
                     )
                     .into_any_element(),
             )

crates/ui/src/components/context_menu.rs šŸ”—

@@ -811,7 +811,7 @@ impl ContextMenu {
                 ListSubHeader::new(header.clone())
                     .inset(true)
                     .end_slot(
-                        Button::new(link_id, label.clone())
+                        Button::new(link_id, label.clone(), cx)
                             .color(Color::Muted)
                             .label_size(LabelSize::Small)
                             .size(ButtonSize::None)

crates/ui/src/components/notification/alert_modal.rs šŸ”—

@@ -59,12 +59,17 @@ impl RenderOnce for AlertModal {
                             .items_center()
                             .gap_1()
                             .child(
-                                Button::new(self.dismiss_label.clone(), self.dismiss_label.clone())
-                                    .color(Color::Muted),
+                                Button::new(
+                                    self.dismiss_label.clone(),
+                                    self.dismiss_label.clone(),
+                                    cx,
+                                )
+                                .color(Color::Muted),
                             )
                             .child(Button::new(
                                 self.primary_action.clone(),
                                 self.primary_action.clone(),
+                                cx,
                             )),
                     ),
             )

crates/ui/src/components/settings_container.rs šŸ”—

@@ -49,7 +49,7 @@ impl Component for SettingsContainer {
         Some("A container for organizing and displaying settings in a structured manner.")
     }
 
-    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         Some(
             v_flex()
                 .gap_6()
@@ -78,7 +78,7 @@ impl Component for SettingsContainer {
                             SettingsContainer::new()
                                 .child(Label::new("Text Setting"))
                                 .child(Checkbox::new("checkbox", ToggleState::Unselected))
-                                .child(Button::new("button", "Click me"))
+                                .child(Button::new("button", "Click me", cx))
                                 .into_any_element(),
                         )],
                     ),

crates/ui/src/components/tab_bar.rs šŸ”—

@@ -165,7 +165,7 @@ impl Component for TabBar {
         Some("A horizontal bar containing tabs for navigation between different views or sections.")
     }
 
-    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         Some(
             v_flex()
                 .gap_6()
@@ -192,11 +192,11 @@ impl Component for TabBar {
                         vec![single_example(
                             "Full TabBar",
                             TabBar::new("full_tab_bar")
-                                .start_child(Button::new("start_button", "Start"))
+                                .start_child(Button::new("start_button", "Start", cx))
                                 .child(Tab::new("tab1"))
                                 .child(Tab::new("tab2"))
                                 .child(Tab::new("tab3"))
-                                .end_child(Button::new("end_button", "End"))
+                                .end_child(Button::new("end_button", "End", cx))
                                 .into_any_element(),
                         )],
                     ),

crates/ui/src/components/tooltip.rs šŸ”—

@@ -283,11 +283,11 @@ impl Component for Tooltip {
         )
     }
 
-    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
         Some(
             example_group(vec![single_example(
                 "Text only",
-                Button::new("delete-example", "Delete")
+                Button::new("delete-example", "Delete", cx)
                     .tooltip(Tooltip::text("This is a tooltip!"))
                     .into_any_element(),
             )])

crates/ui_prompt/src/ui_prompt.rs šŸ”—

@@ -177,7 +177,7 @@ impl Render for ZedPromptRenderer {
             }))
             .child(h_flex().justify_end().gap_2().children(
                 self.actions.iter().enumerate().rev().map(|(ix, action)| {
-                    ui::Button::new(ix, action.clone())
+                    ui::Button::new(ix, action.clone(), cx)
                         .label_size(LabelSize::Large)
                         .style(ButtonStyle::Filled)
                         .when(ix == self.active_action_id, |el| {

crates/welcome/src/multibuffer_hint.rs šŸ”—

@@ -157,7 +157,7 @@ impl Render for MultibufferHint {
                             )),
                     )
                     .child(
-                        Button::new("open_docs", "Learn More")
+                        Button::new("open_docs", "Learn More", cx)
                             .icon(IconName::ArrowUpRight)
                             .icon_size(IconSize::Small)
                             .icon_color(Color::Muted)

crates/welcome/src/welcome.rs šŸ”—

@@ -141,7 +141,7 @@ impl Render for WelcomePage {
                                         ),
                                     )
                                     .child(
-                                        Button::new("choose-theme", "Choose a Theme")
+                                        Button::new("choose-theme", "Choose a Theme", cx)
                                             .icon(IconName::SwatchBook)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
@@ -156,7 +156,7 @@ impl Render for WelcomePage {
                                             })),
                                     )
                                     .child(
-                                        Button::new("choose-keymap", "Choose a Keymap")
+                                        Button::new("choose-keymap", "Choose a Keymap", cx)
                                             .icon(IconName::Keyboard)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
@@ -179,6 +179,7 @@ impl Render for WelcomePage {
                                             Button::new(
                                                 "edit_prediction_onboarding",
                                                 edit_prediction_label,
+                                                cx,
                                             )
                                             .disabled(edit_prediction_provider_is_zed)
                                             .icon(IconName::ZedPredict)
@@ -194,7 +195,7 @@ impl Render for WelcomePage {
                                         )
                                     })
                                     .child(
-                                        Button::new("edit settings", "Edit Settings")
+                                        Button::new("edit settings", "Edit Settings", cx)
                                             .icon(IconName::Settings)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
@@ -220,7 +221,7 @@ impl Render for WelcomePage {
                                     )
                                     .when(cfg!(target_os = "macos"), |el| {
                                         el.child(
-                                            Button::new("install-cli", "Install the CLI")
+                                            Button::new("install-cli", "Install the CLI", cx)
                                                 .icon(IconName::Terminal)
                                                 .icon_size(IconSize::XSmall)
                                                 .icon_color(Color::Muted)
@@ -234,7 +235,7 @@ impl Render for WelcomePage {
                                         )
                                     })
                                     .child(
-                                        Button::new("view-docs", "View Documentation")
+                                        Button::new("view-docs", "View Documentation", cx)
                                             .icon(IconName::FileCode)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
@@ -245,7 +246,7 @@ impl Render for WelcomePage {
                                             })),
                                     )
                                     .child(
-                                        Button::new("explore-extensions", "Explore Extensions")
+                                        Button::new("explore-extensions", "Explore Extensions", cx)
                                             .icon(IconName::Blocks)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)

crates/workspace/src/notifications.rs šŸ”—

@@ -345,7 +345,7 @@ impl Render for LanguageServerPrompt {
                     .child(Label::new(request.message.to_string()).size(LabelSize::Small))
                     .children(request.actions.iter().enumerate().map(|(ix, action)| {
                         let this_handle = cx.entity().clone();
-                        Button::new(ix, action.title.clone())
+                        Button::new(ix, action.title.clone(), cx)
                             .size(ButtonSize::Large)
                             .on_click(move |_, window, cx| {
                                 let this_handle = this_handle.clone();
@@ -442,8 +442,12 @@ impl Render for ErrorMessagePrompt {
                     .when_some(self.label_and_url_button.clone(), |elm, (label, url)| {
                         elm.child(
                             div().mt_2().child(
-                                ui::Button::new("error_message_prompt_notification_button", label)
-                                    .on_click(move |_, _, cx| cx.open_url(&url)),
+                                ui::Button::new(
+                                    "error_message_prompt_notification_button",
+                                    label,
+                                    cx,
+                                )
+                                .on_click(move |_, _, cx| cx.open_url(&url)),
                             ),
                         )
                     }),
@@ -715,7 +719,7 @@ pub mod simple_message_notification {
                     h_flex()
                         .gap_1()
                         .children(self.primary_message.iter().map(|message| {
-                            let mut button = Button::new(message.clone(), message.clone())
+                            let mut button = Button::new(message.clone(), message.clone(), cx)
                                 .label_size(LabelSize::Small)
                                 .on_click(cx.listener(|this, _, window, cx| {
                                     if let Some(on_click) = this.primary_on_click.as_ref() {
@@ -735,7 +739,7 @@ pub mod simple_message_notification {
                             button
                         }))
                         .children(self.secondary_message.iter().map(|message| {
-                            let mut button = Button::new(message.clone(), message.clone())
+                            let mut button = Button::new(message.clone(), message.clone(), cx)
                                 .label_size(LabelSize::Small)
                                 .on_click(cx.listener(|this, _, window, cx| {
                                     if let Some(on_click) = this.secondary_on_click.as_ref() {
@@ -761,7 +765,7 @@ pub mod simple_message_notification {
                                     .zip(self.more_info_url.iter())
                                     .map(|(message, url)| {
                                         let url = url.clone();
-                                        Button::new(message.clone(), message.clone())
+                                        Button::new(message.clone(), message.clone(), cx)
                                             .label_size(LabelSize::Small)
                                             .icon(IconName::ArrowUpRight)
                                             .icon_size(IconSize::Indicator)

crates/workspace/src/theme_preview.rs šŸ”—

@@ -401,7 +401,7 @@ impl ThemePreview {
             .py_2()
             .bg(Self::preview_bg(window, cx))
             .children(ThemePreviewPage::iter().map(|p| {
-                Button::new(ElementId::Name(p.name().into()), p.name())
+                Button::new(ElementId::Name(p.name().into()), p.name(), cx)
                     .on_click(cx.listener(move |this, _, window, cx| {
                         this.current_page = p;
                         cx.notify();

crates/zed/src/zed/component_preview.rs šŸ”—

@@ -772,7 +772,7 @@ impl Render for ComponentPreview {
                             .border_t_1()
                             .border_color(cx.theme().colors().border)
                             .child(
-                                Button::new("toast-test", "Launch Toast")
+                                Button::new("toast-test", "Launch Toast", cx)
                                     .full_width()
                                     .on_click(cx.listener({
                                         move |this, _, _window, cx| {

crates/zed/src/zed/migrate.rs šŸ”—

@@ -235,7 +235,7 @@ impl Render for MigrationBanner {
                     ),
             )
             .child(
-                Button::new("backup-and-migrate", "Backup and Update").on_click(
+                Button::new("backup-and-migrate", "Backup and Update", cx).on_click(
                     move |_, window, cx| {
                         let fs = <dyn Fs>::global(cx);
                         match migration_type {

crates/zeta/src/rate_completion_modal.rs šŸ”—

@@ -313,6 +313,7 @@ impl RateCompletionModal {
                 Button::new(
                     ElementId::Name("suggested-edits".into()),
                     RateCompletionView::SuggestedEdits.name(),
+                    cx,
                 )
                 .label_size(LabelSize::Small)
                 .on_click(cx.listener(move |this, _, _window, cx| {
@@ -325,6 +326,7 @@ impl RateCompletionModal {
                 Button::new(
                     ElementId::Name("raw-input".into()),
                     RateCompletionView::RawInput.name(),
+                    cx,
                 )
                 .label_size(LabelSize::Small)
                 .on_click(cx.listener(move |this, _, _window, cx| {
@@ -489,7 +491,7 @@ impl RateCompletionModal {
                             h_flex()
                                 .gap_1()
                                 .child(
-                                    Button::new("bad", "Bad Completion")
+                                    Button::new("bad", "Bad Completion", cx)
                                         .icon(IconName::ThumbsDown)
                                         .icon_size(IconSize::Small)
                                         .icon_position(IconPosition::Start)
@@ -513,7 +515,7 @@ impl RateCompletionModal {
                                         })),
                                 )
                                 .child(
-                                    Button::new("good", "Good Completion")
+                                    Button::new("good", "Good Completion", cx)
                                         .icon(IconName::ThumbsUp)
                                         .icon_size(IconSize::Small)
                                         .icon_position(IconPosition::Start)