@@ -2,12 +2,18 @@ use crate::{request::PromptUserDeviceFlow, Copilot, Status};
use gpui::{
elements::*,
geometry::rect::RectF,
+ impl_internal_actions,
platform::{WindowBounds, WindowKind, WindowOptions},
AppContext, ClipboardItem, Element, Entity, View, ViewContext, ViewHandle,
};
use settings::Settings;
use theme::ui::modal;
+#[derive(PartialEq, Eq, Debug, Clone)]
+struct ClickedConnect;
+
+impl_internal_actions!(copilot_verification, [ClickedConnect]);
+
#[derive(PartialEq, Eq, Debug, Clone)]
struct CopyUserCode;
@@ -56,6 +62,12 @@ pub fn init(cx: &mut AppContext) {
}
})
.detach();
+
+ cx.add_action(
+ |code_verification: &mut CopilotCodeVerification, _: &ClickedConnect, _| {
+ code_verification.connect_clicked = true;
+ },
+ );
}
fn create_copilot_auth_window(
@@ -81,11 +93,15 @@ fn create_copilot_auth_window(
pub struct CopilotCodeVerification {
status: Status,
+ connect_clicked: bool,
}
impl CopilotCodeVerification {
pub fn new(status: Status) -> Self {
- Self { status }
+ Self {
+ status,
+ connect_clicked: false,
+ }
}
pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
@@ -143,6 +159,7 @@ impl CopilotCodeVerification {
}
fn render_prompting_modal(
+ connect_clicked: bool,
data: &PromptUserDeviceFlow,
style: &theme::Copilot,
cx: &mut gpui::RenderContext<Self>,
@@ -189,13 +206,20 @@ impl CopilotCodeVerification {
.with_style(style.auth.prompting.hint.container.clone())
.boxed(),
theme::ui::cta_button_with_click(
- "Connect to GitHub",
+ if connect_clicked {
+ "Waiting for connection..."
+ } else {
+ "Connect to GitHub"
+ },
style.auth.content_width,
&style.auth.cta_button,
cx,
{
let verification_uri = data.verification_uri.clone();
- move |_, cx| cx.platform().open_url(&verification_uri)
+ move |_, cx| {
+ cx.platform().open_url(&verification_uri);
+ cx.dispatch_action(ClickedConnect)
+ }
},
)
.boxed(),
@@ -343,9 +367,20 @@ impl View for CopilotCodeVerification {
match &self.status {
Status::SigningIn {
prompt: Some(prompt),
- } => Self::render_prompting_modal(&prompt, &style.copilot, cx),
- Status::Unauthorized => Self::render_unauthorized_modal(&style.copilot, cx),
- Status::Authorized => Self::render_enabled_modal(&style.copilot, cx),
+ } => Self::render_prompting_modal(
+ self.connect_clicked,
+ &prompt,
+ &style.copilot,
+ cx,
+ ),
+ Status::Unauthorized => {
+ self.connect_clicked = false;
+ Self::render_unauthorized_modal(&style.copilot, cx)
+ }
+ Status::Authorized => {
+ self.connect_clicked = false;
+ Self::render_enabled_modal(&style.copilot, cx)
+ }
_ => Empty::new().boxed(),
},
])
@@ -24,6 +24,15 @@ const COPILOT_ERROR_TOAST_ID: usize = 1338;
#[derive(Clone, PartialEq)]
pub struct DeployCopilotMenu;
+#[derive(Clone, PartialEq)]
+pub struct DeployCopilotStartMenu;
+
+#[derive(Clone, PartialEq)]
+pub struct HideCopilot;
+
+#[derive(Clone, PartialEq)]
+pub struct InitiateSignIn;
+
#[derive(Clone, PartialEq)]
pub struct ToggleCopilotForLanguage {
language: Arc<str>,
@@ -40,6 +49,9 @@ impl_internal_actions!(
copilot,
[
DeployCopilotMenu,
+ DeployCopilotStartMenu,
+ HideCopilot,
+ InitiateSignIn,
DeployCopilotModal,
ToggleCopilotForLanguage,
ToggleCopilotGlobally,
@@ -48,6 +60,7 @@ impl_internal_actions!(
pub fn init(cx: &mut AppContext) {
cx.add_action(CopilotButton::deploy_copilot_menu);
+ cx.add_action(CopilotButton::deploy_copilot_start_menu);
cx.add_action(
|_: &mut CopilotButton, action: &ToggleCopilotForLanguage, cx| {
let language = action.language.clone();
@@ -73,6 +86,58 @@ pub fn init(cx: &mut AppContext) {
file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
})
});
+
+ cx.add_action(|_: &mut CopilotButton, _: &HideCopilot, cx| {
+ SettingsFile::update(cx, move |file_contents| {
+ file_contents.features.copilot = Some(false)
+ })
+ });
+
+ cx.add_action(|_: &mut CopilotButton, _: &InitiateSignIn, cx| {
+ let Some(copilot) = Copilot::global(cx) else {
+ return;
+ };
+ let status = copilot.read(cx).status();
+
+ match status {
+ Status::Starting { task } => {
+ cx.dispatch_action(workspace::Toast::new(
+ COPILOT_STARTING_TOAST_ID,
+ "Copilot is starting...",
+ ));
+ let window_id = cx.window_id();
+ let task = task.to_owned();
+ cx.spawn(|handle, mut cx| async move {
+ task.await;
+ cx.update(|cx| {
+ if let Some(copilot) = Copilot::global(cx) {
+ let status = copilot.read(cx).status();
+ match status {
+ Status::Authorized => cx.dispatch_action_at(
+ window_id,
+ handle.id(),
+ workspace::Toast::new(
+ COPILOT_STARTING_TOAST_ID,
+ "Copilot has started!",
+ ),
+ ),
+ _ => {
+ cx.dispatch_action_at(
+ window_id,
+ handle.id(),
+ DismissToast::new(COPILOT_STARTING_TOAST_ID),
+ );
+ cx.dispatch_action_at(window_id, handle.id(), SignIn)
+ }
+ }
+ }
+ })
+ })
+ .detach();
+ }
+ _ => cx.dispatch_action(SignIn),
+ }
+ })
}
pub struct CopilotButton {
@@ -109,8 +174,6 @@ impl View for CopilotButton {
.editor_enabled
.unwrap_or(settings.show_copilot_suggestions(None));
- let view_id = cx.view_id();
-
Stack::new()
.with_child(
MouseEventHandler::<Self>::new(0, cx, {
@@ -157,48 +220,13 @@ impl View for CopilotButton {
let status = status.clone();
move |_, cx| match status {
Status::Authorized => cx.dispatch_action(DeployCopilotMenu),
- Status::Starting { ref task } => {
- cx.dispatch_action(workspace::Toast::new(
- COPILOT_STARTING_TOAST_ID,
- "Copilot is starting...",
- ));
- let window_id = cx.window_id();
- let task = task.to_owned();
- cx.spawn(|mut cx| async move {
- task.await;
- cx.update(|cx| {
- if let Some(copilot) = Copilot::global(cx) {
- let status = copilot.read(cx).status();
- match status {
- Status::Authorized => cx.dispatch_action_at(
- window_id,
- view_id,
- workspace::Toast::new(
- COPILOT_STARTING_TOAST_ID,
- "Copilot has started!",
- ),
- ),
- _ => {
- cx.dispatch_action_at(
- window_id,
- view_id,
- DismissToast::new(COPILOT_STARTING_TOAST_ID),
- );
- cx.dispatch_global_action(SignIn)
- }
- }
- }
- })
- })
- .detach();
- }
Status::Error(ref e) => cx.dispatch_action(workspace::Toast::new_action(
COPILOT_ERROR_TOAST_ID,
format!("Copilot can't be started: {}", e),
"Reinstall Copilot",
Reinstall,
)),
- _ => cx.dispatch_action(SignIn),
+ _ => cx.dispatch_action(DeployCopilotStartMenu),
}
})
.with_tooltip::<Self, _>(
@@ -244,6 +272,26 @@ impl CopilotButton {
}
}
+ pub fn deploy_copilot_start_menu(
+ &mut self,
+ _: &DeployCopilotStartMenu,
+ cx: &mut ViewContext<Self>,
+ ) {
+ let mut menu_options = Vec::with_capacity(2);
+
+ menu_options.push(ContextMenuItem::item("Sign In", InitiateSignIn));
+ menu_options.push(ContextMenuItem::item("Hide Copilot", HideCopilot));
+
+ self.popup_menu.update(cx, |menu, cx| {
+ menu.show(
+ Default::default(),
+ AnchorCorner::BottomRight,
+ menu_options,
+ cx,
+ );
+ });
+ }
+
pub fn deploy_copilot_menu(&mut self, _: &DeployCopilotMenu, cx: &mut ViewContext<Self>) {
let settings = cx.global::<Settings>();