From 630d0add190b77da1fa48ea4d1e7d52bc386adca Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Wed, 5 Feb 2025 12:26:11 -0300 Subject: [PATCH] edit predictions: Onboarding funnel telemetry (#24237) Release Notes: - N/A --- Cargo.lock | 1 + crates/editor/src/editor.rs | 14 +++++----- crates/inline_completion_button/Cargo.toml | 1 + .../src/inline_completion_button.rs | 20 +++++++++++++- .../zed/src/zed/inline_completion_registry.rs | 19 ++++++++++---- crates/zeta/src/onboarding_banner.rs | 4 +++ crates/zeta/src/onboarding_modal.rs | 26 ++++++++++++++++--- crates/zeta/src/onboarding_telemetry.rs | 9 +++++++ crates/zeta/src/rate_completion_modal.rs | 2 ++ crates/zeta/src/zeta.rs | 1 + 10 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 crates/zeta/src/onboarding_telemetry.rs diff --git a/Cargo.lock b/Cargo.lock index 5353b9bc44..8da23e96fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6388,6 +6388,7 @@ dependencies = [ "serde_json", "settings", "supermaven", + "telemetry", "theme", "ui", "workspace", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6e62260255..1ecd630dd6 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3946,10 +3946,6 @@ impl Editor { self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx) } - fn toggle_zed_predict_onboarding(&mut self, window: &mut Window, cx: &mut Context) { - window.dispatch_action(zed_actions::OpenZedPredictOnboarding.boxed_clone(), cx); - } - fn do_completion( &mut self, item_ix: Option, @@ -5445,7 +5441,11 @@ impl Editor { .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default()) .on_click(cx.listener(|this, _event, window, cx| { cx.stop_propagation(); - this.toggle_zed_predict_onboarding(window, cx) + this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx); + window.dispatch_action( + zed_actions::OpenZedPredictOnboarding.boxed_clone(), + cx, + ); })) .child( h_flex() @@ -14074,7 +14074,8 @@ impl Editor { .get("vim_mode") == Some(&serde_json::Value::Bool(true)); - let copilot_enabled = all_language_settings(file, cx).inline_completions.provider + let edit_predictions_provider = all_language_settings(file, cx).inline_completions.provider; + let copilot_enabled = edit_predictions_provider == language::language_settings::InlineCompletionProvider::Copilot; let copilot_enabled_for_language = self .buffer @@ -14089,6 +14090,7 @@ impl Editor { vim_mode, copilot_enabled, copilot_enabled_for_language, + edit_predictions_provider, is_via_ssh = project.is_via_ssh(), ); } diff --git a/crates/inline_completion_button/Cargo.toml b/crates/inline_completion_button/Cargo.toml index b5daba3893..e8c51efcaf 100644 --- a/crates/inline_completion_button/Cargo.toml +++ b/crates/inline_completion_button/Cargo.toml @@ -29,6 +29,7 @@ workspace.workspace = true zed_actions.workspace = true zeta.workspace = true client.workspace = true +telemetry.workspace = true [dev-dependencies] copilot = { workspace = true, features = ["test-support"] } diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs index 20cad6ec0b..a2b72ed1c2 100644 --- a/crates/inline_completion_button/src/inline_completion_button.rs +++ b/crates/inline_completion_button/src/inline_completion_button.rs @@ -256,6 +256,10 @@ impl Render for InlineCompletionButton { ) }) .on_click(cx.listener(move |_, _, window, cx| { + telemetry::event!( + "Pending ToS Clicked", + source = "Edit Prediction Status Button" + ); window.dispatch_action( zed_actions::OpenZedPredictOnboarding.boxed_clone(), cx, @@ -426,6 +430,8 @@ impl InlineCompletionButton { if data_collection.is_supported() { let provider = provider.clone(); + let enabled = data_collection.is_enabled(); + menu = menu .separator() .header("Help Improve The Model") @@ -434,9 +440,21 @@ impl InlineCompletionButton { // TODO: We want to add something later that communicates whether // the current project is open-source. ContextMenuEntry::new("Share Training Data") - .toggleable(IconPosition::Start, data_collection.is_enabled()) + .toggleable(IconPosition::Start, enabled) .handler(move |_, cx| { provider.toggle_data_collection(cx); + + if !enabled { + telemetry::event!( + "Data Collection Enabled", + source = "Edit Prediction Status Menu" + ); + } else { + telemetry::event!( + "Data Collection Disabled", + source = "Edit Prediction Status Menu" + ); + } }), ); } diff --git a/crates/zed/src/zed/inline_completion_registry.rs b/crates/zed/src/zed/inline_completion_registry.rs index 58faf1263d..6e2879a6c9 100644 --- a/crates/zed/src/zed/inline_completion_registry.rs +++ b/crates/zed/src/zed/inline_completion_registry.rs @@ -94,7 +94,20 @@ pub fn init(client: Arc, user_store: Entity, cx: &mut App) { let user_store = user_store.clone(); move |cx| { let new_provider = all_language_settings(None, cx).inline_completions.provider; + if new_provider != provider { + let tos_accepted = user_store + .read(cx) + .current_user_has_accepted_terms() + .unwrap_or(false); + + telemetry::event!( + "Edit Prediction Provider Changed", + from = provider, + to = new_provider, + zed_ai_tos_accepted = tos_accepted, + ); + provider = new_provider; assign_inline_completion_providers( &editors, @@ -104,11 +117,7 @@ pub fn init(client: Arc, user_store: Entity, cx: &mut App) { cx, ); - if !user_store - .read(cx) - .current_user_has_accepted_terms() - .unwrap_or(false) - { + if !tos_accepted { match provider { InlineCompletionProvider::Zed => { let Some(window) = cx.active_window() else { diff --git a/crates/zeta/src/onboarding_banner.rs b/crates/zeta/src/onboarding_banner.rs index 26169b2cbf..54a6939d62 100644 --- a/crates/zeta/src/onboarding_banner.rs +++ b/crates/zeta/src/onboarding_banner.rs @@ -6,6 +6,8 @@ use settings::SettingsStore; use ui::{prelude::*, ButtonLike, Tooltip}; use util::ResultExt; +use crate::onboarding_event; + /// Prompts the user to try Zed's Edit Prediction feature pub struct ZedPredictBanner { dismissed: bool, @@ -53,6 +55,7 @@ impl ZedPredictBanner { } fn dismiss(&mut self, cx: &mut Context) { + onboarding_event!("Banner Dismissed"); persist_dismissed(cx); self.dismissed = true; cx.notify(); @@ -107,6 +110,7 @@ impl Render for ZedPredictBanner { ), ) .on_click(|_, window, cx| { + onboarding_event!("Banner Clicked"); window.dispatch_action(Box::new(zed_actions::OpenZedPredictOnboarding), cx) }), ) diff --git a/crates/zeta/src/onboarding_modal.rs b/crates/zeta/src/onboarding_modal.rs index b9e214508c..c17289b78f 100644 --- a/crates/zeta/src/onboarding_modal.rs +++ b/crates/zeta/src/onboarding_modal.rs @@ -1,6 +1,6 @@ use std::{sync::Arc, time::Duration}; -use crate::ZED_PREDICT_DATA_COLLECTION_CHOICE; +use crate::{onboarding_event, ZED_PREDICT_DATA_COLLECTION_CHOICE}; use client::{Client, UserStore}; use db::kvp::KEY_VALUE_STORE; use feature_flags::FeatureFlagAppExt as _; @@ -61,16 +61,22 @@ impl ZedPredictModal { fn view_terms(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { cx.open_url("https://zed.dev/terms-of-service"); cx.notify(); + + onboarding_event!("ToS Link Clicked"); } fn view_blog(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { cx.open_url("https://zed.dev/blog/"); // TODO Add the link when live cx.notify(); + + onboarding_event!("Blog Link clicked"); } fn inline_completions_doc(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { cx.open_url("https://zed.dev/docs/configuring-zed#inline-completions"); cx.notify(); + + onboarding_event!("Docs Link Clicked"); } fn accept_and_enable(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { @@ -106,6 +112,11 @@ impl ZedPredictModal { }) }) .detach_and_notify_err(window, cx); + + onboarding_event!( + "Enable Clicked", + data_collection_opted_in = self.data_collection_opted_in, + ); } fn sign_in(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { @@ -122,12 +133,15 @@ impl ZedPredictModal { this.update(&mut cx, |this, cx| { this.sign_in_status = status; + onboarding_event!("Signed In"); cx.notify() })?; result }) .detach_and_notify_err(window, cx); + + onboarding_event!("Sign In Clicked"); } fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { @@ -159,6 +173,7 @@ impl Render for ZedPredictModal { .track_focus(&self.focus_handle(cx)) .on_action(cx.listener(Self::cancel)) .on_action(cx.listener(|_, _: &menu::Cancel, _window, cx| { + onboarding_event!("Cancelled", trigger = "Action"); cx.emit(DismissEvent); })) .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| { @@ -241,6 +256,7 @@ impl Render for ZedPredictModal { .child(h_flex().absolute().top_2().right_2().child( IconButton::new("cancel", IconName::X).on_click(cx.listener( |_, _: &ClickEvent, _window, cx| { + onboarding_event!("Cancelled", trigger = "X click"); cx.emit(DismissEvent); }, )), @@ -302,7 +318,7 @@ impl Render for ZedPredictModal { .label("Read and accept the") .on_click(cx.listener(move |this, state, _window, cx| { this.terms_of_service = *state == ToggleState::Selected; - cx.notify() + cx.notify(); })), ) .child( @@ -340,7 +356,11 @@ impl Render for ZedPredictModal { .on_click(cx.listener(|this, _, _, cx| { this.data_collection_expanded = !this.data_collection_expanded; - cx.notify() + cx.notify(); + + if this.data_collection_expanded { + onboarding_event!("Data Collection Learn More Clicked"); + } })), ), ) diff --git a/crates/zeta/src/onboarding_telemetry.rs b/crates/zeta/src/onboarding_telemetry.rs new file mode 100644 index 0000000000..3c7d5e1442 --- /dev/null +++ b/crates/zeta/src/onboarding_telemetry.rs @@ -0,0 +1,9 @@ +#[macro_export] +macro_rules! onboarding_event { + ($name:expr) => { + telemetry::event!($name, source = "Edit Prediction Onboarding"); + }; + ($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => { + telemetry::event!($name, source = "Edit Prediction Onboarding", $($key $(= $value)?),+); + }; +} diff --git a/crates/zeta/src/rate_completion_modal.rs b/crates/zeta/src/rate_completion_modal.rs index 073388e22c..dda838c21b 100644 --- a/crates/zeta/src/rate_completion_modal.rs +++ b/crates/zeta/src/rate_completion_modal.rs @@ -52,6 +52,8 @@ impl RateCompletionModal { pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context) { if let Some(zeta) = Zeta::global(cx) { workspace.toggle_modal(window, cx, |_window, cx| RateCompletionModal::new(zeta, cx)); + + telemetry::event!("Rate Completion Modal Open", source = "Edit Prediction"); } } diff --git a/crates/zeta/src/zeta.rs b/crates/zeta/src/zeta.rs index 7aa2dc212a..584e4a8bb8 100644 --- a/crates/zeta/src/zeta.rs +++ b/crates/zeta/src/zeta.rs @@ -3,6 +3,7 @@ mod init; mod license_detection; mod onboarding_banner; mod onboarding_modal; +mod onboarding_telemetry; mod rate_completion_modal; pub(crate) use completion_diff_element::*;