Add Sign Out link for Supermaven (#14834)

Kevin Wang created

Adds a menu item to sign out from a linked Supermaven account.


![image](https://github.com/user-attachments/assets/6af3c39f-9ca4-4ac2-a5c0-c7cb3c3aaac2)


Release Notes:

- Added the ability to sign out of a Supermaven account ([#12715(https://github.com/zed-industries/zed/issues/12715))

Change summary

crates/inline_completion_button/src/inline_completion_button.rs |  4 
crates/supermaven/src/messages.rs                               |  1 
crates/supermaven/src/supermaven.rs                             | 25 ++
3 files changed, 28 insertions(+), 2 deletions(-)

Detailed changes

crates/inline_completion_button/src/inline_completion_button.rs 🔗

@@ -298,7 +298,9 @@ impl InlineCompletionButton {
 
     fn build_supermaven_context_menu(&self, cx: &mut ViewContext<Self>) -> View<ContextMenu> {
         ContextMenu::build(cx, |menu, cx| {
-            self.build_language_settings_menu(menu, cx).separator()
+            self.build_language_settings_menu(menu, cx)
+                .separator()
+                .action("Sign Out", supermaven::SignOut.boxed_clone())
         })
     }
 

crates/supermaven/src/messages.rs 🔗

@@ -14,6 +14,7 @@ pub enum OutboundMessage {
     StateUpdate(StateUpdateMessage),
     #[allow(dead_code)]
     UseFreeVersion,
+    Logout,
 }
 
 #[derive(Debug, Serialize, Deserialize)]

crates/supermaven/src/supermaven.rs 🔗

@@ -9,7 +9,9 @@ use client::{proto, Client};
 use collections::BTreeMap;
 
 use futures::{channel::mpsc, io::BufReader, AsyncBufReadExt, StreamExt};
-use gpui::{AppContext, AsyncAppContext, EntityId, Global, Model, ModelContext, Task, WeakModel};
+use gpui::{
+    actions, AppContext, AsyncAppContext, EntityId, Global, Model, ModelContext, Task, WeakModel,
+};
 use language::{
     language_settings::all_language_settings, Anchor, Buffer, BufferSnapshot, ToOffset,
 };
@@ -25,6 +27,8 @@ use std::{path::PathBuf, process::Stdio, sync::Arc};
 use ui::prelude::*;
 use util::ResultExt;
 
+actions!(supermaven, [SignOut]);
+
 pub fn init(client: Arc<Client>, cx: &mut AppContext) {
     let supermaven = cx.new_model(|_| Supermaven::Starting);
     Supermaven::set_global(supermaven.clone(), cx);
@@ -46,6 +50,12 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
         }
     })
     .detach();
+
+    cx.on_action(|_: &SignOut, cx| {
+        if let Some(supermaven) = Supermaven::global(cx) {
+            supermaven.update(cx, |supermaven, _cx| supermaven.sign_out());
+        }
+    });
 }
 
 pub enum Supermaven {
@@ -175,6 +185,19 @@ impl Supermaven {
             None
         }
     }
+
+    pub fn sign_out(&mut self) {
+        if let Self::Spawned(agent) = self {
+            agent
+                .outgoing_tx
+                .unbounded_send(OutboundMessage::Logout)
+                .ok();
+            // The account status will get set to RequiresActivation or Ready when the next
+            // message from the agent comes in. Until that happens, set the status to Unknown
+            // to disable the button.
+            agent.account_status = AccountStatus::Unknown;
+        }
+    }
 }
 
 fn find_relevant_completion<'a>(