From fe7a1aa921a82ad5be9064bb6524c80a667beb8b Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 18 Dec 2025 20:06:58 -0500 Subject: [PATCH] Remove oauth device flow for now --- .../workspace/src/oauth_device_flow_modal.rs | 294 ------------------ 1 file changed, 294 deletions(-) delete mode 100644 crates/workspace/src/oauth_device_flow_modal.rs diff --git a/crates/workspace/src/oauth_device_flow_modal.rs b/crates/workspace/src/oauth_device_flow_modal.rs deleted file mode 100644 index 629f3ca4b7ded924bbace85d7b2c36ad2c57c2b5..0000000000000000000000000000000000000000 --- a/crates/workspace/src/oauth_device_flow_modal.rs +++ /dev/null @@ -1,294 +0,0 @@ -use gpui::{ - Animation, AnimationExt, App, ClipboardItem, Context, DismissEvent, Element, Entity, - EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, MouseDownEvent, - ParentElement, Render, SharedString, Styled, Subscription, Transformation, Window, div, - percentage, rems, svg, -}; -use menu; -use std::time::Duration; -use ui::{Button, Icon, IconName, Label, Vector, VectorName, prelude::*}; - -use crate::ModalView; - -/// Configuration for the OAuth device flow modal. -/// This allows extensions to specify the text and appearance of the modal. -#[derive(Clone)] -pub struct OAuthDeviceFlowModalConfig { - /// The user code to display (e.g., "ABC-123"). - pub user_code: String, - /// The URL the user needs to visit to authorize (for the "Connect" button). - pub verification_url: String, - /// The headline text for the modal (e.g., "Use GitHub Copilot in Zed."). - pub headline: String, - /// A description to show below the headline. - pub description: String, - /// Label for the connect button (e.g., "Connect to GitHub"). - pub connect_button_label: String, - /// Success headline shown when authorization completes. - pub success_headline: String, - /// Success message shown when authorization completes. - pub success_message: String, - /// Optional path to an SVG icon file (absolute path on disk). - pub icon_path: Option, -} - -/// The current status of the OAuth device flow. -#[derive(Clone, Debug)] -pub enum OAuthDeviceFlowStatus { - /// Waiting for user to click connect and authorize. - Prompting, - /// User clicked connect, waiting for authorization. - WaitingForAuthorization, - /// Successfully authorized. - Authorized, - /// Authorization failed with an error message. - Failed(String), -} - -/// Shared state for the OAuth device flow that can be observed by the modal. -pub struct OAuthDeviceFlowState { - pub config: OAuthDeviceFlowModalConfig, - pub status: OAuthDeviceFlowStatus, -} - -impl EventEmitter<()> for OAuthDeviceFlowState {} - -impl OAuthDeviceFlowState { - pub fn new(config: OAuthDeviceFlowModalConfig) -> Self { - Self { - config, - status: OAuthDeviceFlowStatus::Prompting, - } - } - - /// Update the status of the OAuth flow. - pub fn set_status(&mut self, status: OAuthDeviceFlowStatus, cx: &mut Context) { - self.status = status; - cx.emit(()); - cx.notify(); - } -} - -/// A generic OAuth device flow modal that can be used by extensions. -pub struct OAuthDeviceFlowModal { - state: Entity, - connect_clicked: bool, - focus_handle: FocusHandle, - _subscription: Subscription, -} - -impl Focusable for OAuthDeviceFlowModal { - fn focus_handle(&self, _: &App) -> FocusHandle { - self.focus_handle.clone() - } -} - -impl EventEmitter for OAuthDeviceFlowModal {} - -impl ModalView for OAuthDeviceFlowModal {} - -impl OAuthDeviceFlowModal { - pub fn new(state: Entity, cx: &mut Context) -> Self { - let subscription = cx.observe(&state, |_, _, cx| { - cx.notify(); - }); - - Self { - state, - connect_clicked: false, - focus_handle: cx.focus_handle(), - _subscription: subscription, - } - } - - fn render_icon(&self, cx: &mut Context) -> impl IntoElement { - let state = self.state.read(cx); - let icon_color = Color::Custom(cx.theme().colors().icon); - // Match ZedXCopilot visual appearance - let icon_size = rems(2.5); - let plus_size = rems(0.875); - // The "+" in ZedXCopilot SVG has fill-opacity="0.5" - let plus_color = cx.theme().colors().icon.opacity(0.5); - - if let Some(icon_path) = &state.config.icon_path { - // Show "[Provider Icon] + [Zed Logo]" format to match built-in Copilot modal - h_flex() - .gap_2() - .items_center() - .child( - Icon::from_external_svg(icon_path.clone()) - .size(ui::IconSize::Custom(icon_size)) - .color(icon_color), - ) - .child( - svg() - .size(plus_size) - .path("icons/plus.svg") - .text_color(plus_color), - ) - .child(Vector::new(VectorName::ZedLogo, icon_size, icon_size).color(icon_color)) - .into_any_element() - } else { - // Fallback to just Zed logo if no provider icon - Vector::new(VectorName::ZedLogo, icon_size, icon_size) - .color(icon_color) - .into_any_element() - } - } - - fn render_device_code(&self, cx: &mut Context) -> impl IntoElement { - let state = self.state.read(cx); - let user_code = state.config.user_code.clone(); - let copied = cx - .read_from_clipboard() - .map(|item| item.text().as_ref() == Some(&user_code)) - .unwrap_or(false); - let user_code_for_click = user_code.clone(); - - h_flex() - .w_full() - .p_1() - .border_1() - .border_muted(cx) - .rounded_sm() - .cursor_pointer() - .justify_between() - .on_mouse_down(gpui::MouseButton::Left, move |_, window, cx| { - cx.write_to_clipboard(ClipboardItem::new_string(user_code_for_click.clone())); - window.refresh(); - }) - .child(div().flex_1().child(Label::new(user_code))) - .child(div().flex_none().px_1().child(Label::new(if copied { - "Copied!" - } else { - "Copy" - }))) - } - - fn render_prompting_modal(&self, cx: &mut Context) -> impl Element { - let (connect_button_label, verification_url, headline, description) = { - let state = self.state.read(cx); - let label = if self.connect_clicked { - "Waiting for connection...".to_string() - } else { - state.config.connect_button_label.clone() - }; - ( - label, - state.config.verification_url.clone(), - state.config.headline.clone(), - state.config.description.clone(), - ) - }; - - v_flex() - .flex_1() - .gap_2() - .items_center() - .child(Headline::new(headline).size(HeadlineSize::Large)) - .child(Label::new(description).color(Color::Muted)) - .child(self.render_device_code(cx)) - .child( - Label::new("Paste this code into GitHub after clicking the button below.") - .size(ui::LabelSize::Small), - ) - .child( - Button::new("connect-button", connect_button_label) - .on_click(cx.listener(move |this, _, _window, cx| { - cx.open_url(&verification_url); - this.connect_clicked = true; - })) - .full_width() - .style(ButtonStyle::Filled), - ) - .child( - Button::new("cancel-button", "Cancel") - .full_width() - .on_click(cx.listener(|_, _, _, cx| { - cx.emit(DismissEvent); - })), - ) - } - - fn render_authorized_modal(&self, cx: &mut Context) -> impl Element { - let state = self.state.read(cx); - let success_headline = state.config.success_headline.clone(); - let success_message = state.config.success_message.clone(); - - v_flex() - .gap_2() - .child(Headline::new(success_headline).size(HeadlineSize::Large)) - .child(Label::new(success_message)) - .child( - Button::new("done-button", "Done") - .full_width() - .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))), - ) - } - - fn render_failed_modal(&self, error: &str, cx: &mut Context) -> impl Element { - v_flex() - .gap_2() - .child(Headline::new("Authorization Failed").size(HeadlineSize::Large)) - .child(Label::new(error.to_string()).color(Color::Error)) - .child( - Button::new("close-button", "Close") - .full_width() - .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))), - ) - } - - fn render_loading(window: &mut Window, _cx: &mut Context) -> impl Element { - let loading_icon = svg() - .size_8() - .path(IconName::ArrowCircle.path()) - .text_color(window.text_style().color) - .with_animation( - "icon_circle_arrow", - Animation::new(Duration::from_secs(2)).repeat(), - |svg, delta| svg.with_transformation(Transformation::rotate(percentage(delta))), - ); - - h_flex().justify_center().child(loading_icon) - } -} - -impl Render for OAuthDeviceFlowModal { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let status = self.state.read(cx).status.clone(); - - let prompt = match &status { - OAuthDeviceFlowStatus::Prompting => self.render_prompting_modal(cx).into_any_element(), - OAuthDeviceFlowStatus::WaitingForAuthorization => { - if self.connect_clicked { - self.render_prompting_modal(cx).into_any_element() - } else { - Self::render_loading(window, cx).into_any_element() - } - } - OAuthDeviceFlowStatus::Authorized => { - self.render_authorized_modal(cx).into_any_element() - } - OAuthDeviceFlowStatus::Failed(error) => { - self.render_failed_modal(error, cx).into_any_element() - } - }; - - v_flex() - .id("oauth-device-flow-modal") - .track_focus(&self.focus_handle(cx)) - .elevation_3(cx) - .w_96() - .items_center() - .p_4() - .gap_2() - .on_action(cx.listener(|_, _: &menu::Cancel, _, cx| { - cx.emit(DismissEvent); - })) - .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, cx| { - window.focus(&this.focus_handle, cx); - })) - .child(self.render_icon(cx)) - .child(prompt) - } -}