Require accepting ToS when enabling zeta (#23255)
Note: Design hasn't been reviewed yet, but the logic is done When the user switches the inline completion provider to `zed`, we'll show a modal prompting them to accept terms if they haven't done so: https://github.com/user-attachments/assets/3fc6d368-c00a-4dcb-9484-fbbbb5eb859e If they dismiss the modal, they'll be able to get to it again from the inline completion button: https://github.com/user-attachments/assets/cf842778-5538-4e06-9ed8-21579981cc47 This also stops zeta sending requests that will fail immediately when ToS are not accepted. Release Notes: - N/A --------- Co-authored-by: Richard <richard@zed.dev> Co-authored-by: Danilo Leal <daniloleal09@gmail.com> Co-authored-by: Joao <joao@zed.dev>
This commit is contained in:
parent
5bb696e6d7
commit
919803a4f4
19 changed files with 427 additions and 23 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -3959,6 +3959,7 @@ dependencies = [
|
||||||
"util",
|
"util",
|
||||||
"uuid",
|
"uuid",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
"zed_predict_tos",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6304,6 +6305,7 @@ name = "inline_completion_button"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"client",
|
||||||
"copilot",
|
"copilot",
|
||||||
"editor",
|
"editor",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
|
@ -6323,6 +6325,7 @@ dependencies = [
|
||||||
"ui",
|
"ui",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
|
"zed_predict_tos",
|
||||||
"zeta",
|
"zeta",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -16337,6 +16340,7 @@ dependencies = [
|
||||||
"winresource",
|
"winresource",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
|
"zed_predict_tos",
|
||||||
"zeta",
|
"zeta",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -16450,6 +16454,17 @@ dependencies = [
|
||||||
"zed_extension_api 0.1.0",
|
"zed_extension_api 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zed_predict_tos"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"client",
|
||||||
|
"gpui",
|
||||||
|
"menu",
|
||||||
|
"ui",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_prisma"
|
name = "zed_prisma"
|
||||||
version = "0.0.4"
|
version = "0.0.4"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"crates/activity_indicator",
|
"crates/activity_indicator",
|
||||||
|
"crates/zed_predict_tos",
|
||||||
"crates/anthropic",
|
"crates/anthropic",
|
||||||
"crates/assets",
|
"crates/assets",
|
||||||
"crates/assistant",
|
"crates/assistant",
|
||||||
|
@ -198,6 +199,7 @@ edition = "2021"
|
||||||
|
|
||||||
activity_indicator = { path = "crates/activity_indicator" }
|
activity_indicator = { path = "crates/activity_indicator" }
|
||||||
ai = { path = "crates/ai" }
|
ai = { path = "crates/ai" }
|
||||||
|
zed_predict_tos = { path = "crates/zed_predict_tos" }
|
||||||
anthropic = { path = "crates/anthropic" }
|
anthropic = { path = "crates/anthropic" }
|
||||||
assets = { path = "crates/assets" }
|
assets = { path = "crates/assets" }
|
||||||
assistant = { path = "crates/assistant" }
|
assistant = { path = "crates/assistant" }
|
||||||
|
|
|
@ -875,5 +875,12 @@
|
||||||
"cmd-shift-enter": "zeta::ThumbsUpActiveCompletion",
|
"cmd-shift-enter": "zeta::ThumbsUpActiveCompletion",
|
||||||
"cmd-shift-backspace": "zeta::ThumbsDownActiveCompletion"
|
"cmd-shift-backspace": "zeta::ThumbsDownActiveCompletion"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ZedPredictTos",
|
||||||
|
"use_key_equivalents": true,
|
||||||
|
"bindings": {
|
||||||
|
"escape": "menu::Cancel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -122,6 +122,9 @@ pub enum Event {
|
||||||
},
|
},
|
||||||
ShowContacts,
|
ShowContacts,
|
||||||
ParticipantIndicesChanged,
|
ParticipantIndicesChanged,
|
||||||
|
TermsStatusUpdated {
|
||||||
|
accepted: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -210,10 +213,24 @@ impl UserStore {
|
||||||
staff,
|
staff,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.update(cx, |this, _| {
|
this.update(cx, |this, cx| {
|
||||||
this.set_current_user_accepted_tos_at(
|
let accepted_tos_at = {
|
||||||
info.accepted_tos_at,
|
#[cfg(debug_assertions)]
|
||||||
);
|
if std::env::var("ZED_IGNORE_ACCEPTED_TOS").is_ok()
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
info.accepted_tos_at
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
info.accepted_tos_at
|
||||||
|
};
|
||||||
|
|
||||||
|
this.set_current_user_accepted_tos_at(accepted_tos_at);
|
||||||
|
cx.emit(Event::TermsStatusUpdated {
|
||||||
|
accepted: accepted_tos_at.is_some(),
|
||||||
|
});
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
|
@ -704,8 +721,9 @@ impl UserStore {
|
||||||
.await
|
.await
|
||||||
.context("error accepting tos")?;
|
.context("error accepting tos")?;
|
||||||
|
|
||||||
this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at))
|
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at));
|
||||||
|
cx.emit(Event::TermsStatusUpdated { accepted: true });
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("client not found"))
|
Err(anyhow!("client not found"))
|
||||||
|
|
|
@ -88,6 +88,7 @@ url.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
zed_predict_tos.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
|
|
|
@ -616,6 +616,25 @@ impl CompletionsMenu {
|
||||||
)
|
)
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
CompletionEntry::InlineCompletionHint(
|
||||||
|
hint @ InlineCompletionMenuHint::PendingTermsAcceptance,
|
||||||
|
) => div().min_w(px(250.)).max_w(px(500.)).child(
|
||||||
|
ListItem::new("inline-completion")
|
||||||
|
.inset(true)
|
||||||
|
.toggle_state(item_ix == selected_item)
|
||||||
|
.start_slot(Icon::new(IconName::ZedPredict))
|
||||||
|
.child(
|
||||||
|
base_label.child(
|
||||||
|
StyledText::new(hint.label())
|
||||||
|
.with_highlights(&style.text, None),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_click(cx.listener(move |editor, _event, cx| {
|
||||||
|
cx.stop_propagation();
|
||||||
|
editor.toggle_zed_predict_tos(cx);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
|
||||||
CompletionEntry::InlineCompletionHint(
|
CompletionEntry::InlineCompletionHint(
|
||||||
hint @ InlineCompletionMenuHint::Loaded { .. },
|
hint @ InlineCompletionMenuHint::Loaded { .. },
|
||||||
) => div().min_w(px(250.)).max_w(px(500.)).child(
|
) => div().min_w(px(250.)).max_w(px(500.)).child(
|
||||||
|
|
|
@ -70,6 +70,7 @@ pub use element::{
|
||||||
};
|
};
|
||||||
use futures::{future, FutureExt};
|
use futures::{future, FutureExt};
|
||||||
use fuzzy::StringMatchCandidate;
|
use fuzzy::StringMatchCandidate;
|
||||||
|
use zed_predict_tos::ZedPredictTos;
|
||||||
|
|
||||||
use code_context_menus::{
|
use code_context_menus::{
|
||||||
AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
|
AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
|
||||||
|
@ -459,6 +460,7 @@ type CompletionId = usize;
|
||||||
enum InlineCompletionMenuHint {
|
enum InlineCompletionMenuHint {
|
||||||
Loading,
|
Loading,
|
||||||
Loaded { text: InlineCompletionText },
|
Loaded { text: InlineCompletionText },
|
||||||
|
PendingTermsAcceptance,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,6 +470,7 @@ impl InlineCompletionMenuHint {
|
||||||
InlineCompletionMenuHint::Loading | InlineCompletionMenuHint::Loaded { .. } => {
|
InlineCompletionMenuHint::Loading | InlineCompletionMenuHint::Loaded { .. } => {
|
||||||
"Edit Prediction"
|
"Edit Prediction"
|
||||||
}
|
}
|
||||||
|
InlineCompletionMenuHint::PendingTermsAcceptance => "Accept Terms of Service",
|
||||||
InlineCompletionMenuHint::None => "No Prediction",
|
InlineCompletionMenuHint::None => "No Prediction",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3828,6 +3831,14 @@ impl Editor {
|
||||||
self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
|
self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_zed_predict_tos(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
let (Some(workspace), Some(project)) = (self.workspace(), self.project.as_ref()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZedPredictTos::toggle(workspace, project.read(cx).user_store().clone(), cx);
|
||||||
|
}
|
||||||
|
|
||||||
fn do_completion(
|
fn do_completion(
|
||||||
&mut self,
|
&mut self,
|
||||||
item_ix: Option<usize>,
|
item_ix: Option<usize>,
|
||||||
|
@ -3851,6 +3862,14 @@ impl Editor {
|
||||||
self.context_menu_next(&Default::default(), cx);
|
self.context_menu_next(&Default::default(), cx);
|
||||||
return Some(Task::ready(Ok(())));
|
return Some(Task::ready(Ok(())));
|
||||||
}
|
}
|
||||||
|
Some(CompletionEntry::InlineCompletionHint(
|
||||||
|
InlineCompletionMenuHint::PendingTermsAcceptance,
|
||||||
|
)) => {
|
||||||
|
drop(entries);
|
||||||
|
drop(context_menu);
|
||||||
|
self.toggle_zed_predict_tos(cx);
|
||||||
|
return Some(Task::ready(Ok(())));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4974,6 +4993,8 @@ impl Editor {
|
||||||
Some(InlineCompletionMenuHint::Loaded { text })
|
Some(InlineCompletionMenuHint::Loaded { text })
|
||||||
} else if provider.is_refreshing(cx) {
|
} else if provider.is_refreshing(cx) {
|
||||||
Some(InlineCompletionMenuHint::Loading)
|
Some(InlineCompletionMenuHint::Loading)
|
||||||
|
} else if provider.needs_terms_acceptance(cx) {
|
||||||
|
Some(InlineCompletionMenuHint::PendingTermsAcceptance)
|
||||||
} else {
|
} else {
|
||||||
Some(InlineCompletionMenuHint::None)
|
Some(InlineCompletionMenuHint::None)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ pub trait InlineCompletionProvider: 'static + Sized {
|
||||||
debounce: bool,
|
debounce: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
);
|
);
|
||||||
|
fn needs_terms_acceptance(&self, _cx: &AppContext) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
fn cycle(
|
fn cycle(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: Model<Buffer>,
|
buffer: Model<Buffer>,
|
||||||
|
@ -64,6 +67,7 @@ pub trait InlineCompletionProviderHandle {
|
||||||
) -> bool;
|
) -> bool;
|
||||||
fn show_completions_in_menu(&self) -> bool;
|
fn show_completions_in_menu(&self) -> bool;
|
||||||
fn show_completions_in_normal_mode(&self) -> bool;
|
fn show_completions_in_normal_mode(&self) -> bool;
|
||||||
|
fn needs_terms_acceptance(&self, cx: &AppContext) -> bool;
|
||||||
fn is_refreshing(&self, cx: &AppContext) -> bool;
|
fn is_refreshing(&self, cx: &AppContext) -> bool;
|
||||||
fn refresh(
|
fn refresh(
|
||||||
&self,
|
&self,
|
||||||
|
@ -118,6 +122,10 @@ where
|
||||||
self.read(cx).is_enabled(buffer, cursor_position, cx)
|
self.read(cx).is_enabled(buffer, cursor_position, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_terms_acceptance(&self, cx: &AppContext) -> bool {
|
||||||
|
self.read(cx).needs_terms_acceptance(cx)
|
||||||
|
}
|
||||||
|
|
||||||
fn is_refreshing(&self, cx: &AppContext) -> bool {
|
fn is_refreshing(&self, cx: &AppContext) -> bool {
|
||||||
self.read(cx).is_refreshing()
|
self.read(cx).is_refreshing()
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ ui.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
zed_actions.workspace = true
|
||||||
zeta.workspace = true
|
zeta.workspace = true
|
||||||
|
client.workspace = true
|
||||||
|
zed_predict_tos.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
copilot = { workspace = true, features = ["test-support"] }
|
copilot = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use client::UserStore;
|
||||||
use copilot::{Copilot, Status};
|
use copilot::{Copilot, Status};
|
||||||
use editor::{scroll::Autoscroll, Editor};
|
use editor::{scroll::Autoscroll, Editor};
|
||||||
use feature_flags::{FeatureFlagAppExt, PredictEditsFeatureFlag};
|
use feature_flags::{FeatureFlagAppExt, PredictEditsFeatureFlag};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, pulsating_between, Action, Animation, AnimationExt, AppContext,
|
actions, div, pulsating_between, Action, Animation, AnimationExt, AppContext,
|
||||||
AsyncWindowContext, Corner, Entity, IntoElement, ParentElement, Render, Subscription, View,
|
AsyncWindowContext, Corner, Entity, IntoElement, Model, ParentElement, Render, Subscription,
|
||||||
ViewContext, WeakView, WindowContext,
|
View, ViewContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{
|
language_settings::{
|
||||||
|
@ -17,6 +18,7 @@ use language::{
|
||||||
use settings::{update_settings_file, Settings, SettingsStore};
|
use settings::{update_settings_file, Settings, SettingsStore};
|
||||||
use std::{path::Path, sync::Arc, time::Duration};
|
use std::{path::Path, sync::Arc, time::Duration};
|
||||||
use supermaven::{AccountStatus, Supermaven};
|
use supermaven::{AccountStatus, Supermaven};
|
||||||
|
use ui::{ActiveTheme as _, ButtonLike, Color, Icon, IconWithIndicator, Indicator};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
create_and_open_local_file,
|
create_and_open_local_file,
|
||||||
item::ItemHandle,
|
item::ItemHandle,
|
||||||
|
@ -27,6 +29,7 @@ use workspace::{
|
||||||
StatusItemView, Toast, Workspace,
|
StatusItemView, Toast, Workspace,
|
||||||
};
|
};
|
||||||
use zed_actions::OpenBrowser;
|
use zed_actions::OpenBrowser;
|
||||||
|
use zed_predict_tos::ZedPredictTos;
|
||||||
use zeta::RateCompletionModal;
|
use zeta::RateCompletionModal;
|
||||||
|
|
||||||
actions!(zeta, [RateCompletions]);
|
actions!(zeta, [RateCompletions]);
|
||||||
|
@ -43,6 +46,7 @@ pub struct InlineCompletionButton {
|
||||||
inline_completion_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>,
|
inline_completion_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SupermavenButtonStatus {
|
enum SupermavenButtonStatus {
|
||||||
|
@ -206,6 +210,45 @@ impl Render for InlineCompletionButton {
|
||||||
return div();
|
return div();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.user_store
|
||||||
|
.read(cx)
|
||||||
|
.current_user_has_accepted_terms()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
let workspace = self.workspace.clone();
|
||||||
|
let user_store = self.user_store.clone();
|
||||||
|
|
||||||
|
return div().child(
|
||||||
|
ButtonLike::new("zeta-pending-tos-icon")
|
||||||
|
.child(
|
||||||
|
IconWithIndicator::new(
|
||||||
|
Icon::new(IconName::ZedPredict),
|
||||||
|
Some(Indicator::dot().color(Color::Error)),
|
||||||
|
)
|
||||||
|
.indicator_border_color(Some(
|
||||||
|
cx.theme().colors().status_bar_background,
|
||||||
|
))
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
.tooltip(|cx| {
|
||||||
|
Tooltip::with_meta(
|
||||||
|
"Edit Predictions",
|
||||||
|
None,
|
||||||
|
"Read Terms of Service",
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.on_click(cx.listener(move |_, _, cx| {
|
||||||
|
let user_store = user_store.clone();
|
||||||
|
|
||||||
|
if let Some(workspace) = workspace.upgrade() {
|
||||||
|
ZedPredictTos::toggle(workspace, user_store, cx);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let this = cx.view().clone();
|
let this = cx.view().clone();
|
||||||
let button = IconButton::new("zeta", IconName::ZedPredict)
|
let button = IconButton::new("zeta", IconName::ZedPredict)
|
||||||
.tooltip(|cx| Tooltip::text("Edit Prediction", cx));
|
.tooltip(|cx| Tooltip::text("Edit Prediction", cx));
|
||||||
|
@ -244,6 +287,7 @@ impl InlineCompletionButton {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if let Some(copilot) = Copilot::global(cx) {
|
if let Some(copilot) = Copilot::global(cx) {
|
||||||
|
@ -261,6 +305,7 @@ impl InlineCompletionButton {
|
||||||
inline_completion_provider: None,
|
inline_completion_provider: None,
|
||||||
workspace,
|
workspace,
|
||||||
fs,
|
fs,
|
||||||
|
user_store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,8 @@ pub enum IconSize {
|
||||||
#[default]
|
#[default]
|
||||||
/// 16px
|
/// 16px
|
||||||
Medium,
|
Medium,
|
||||||
|
/// 48px
|
||||||
|
XLarge,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IconSize {
|
impl IconSize {
|
||||||
|
@ -77,6 +79,7 @@ impl IconSize {
|
||||||
IconSize::XSmall => rems_from_px(12.),
|
IconSize::XSmall => rems_from_px(12.),
|
||||||
IconSize::Small => rems_from_px(14.),
|
IconSize::Small => rems_from_px(14.),
|
||||||
IconSize::Medium => rems_from_px(16.),
|
IconSize::Medium => rems_from_px(16.),
|
||||||
|
IconSize::XLarge => rems_from_px(48.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +95,7 @@ impl IconSize {
|
||||||
IconSize::XSmall => DynamicSpacing::Base02.px(cx),
|
IconSize::XSmall => DynamicSpacing::Base02.px(cx),
|
||||||
IconSize::Small => DynamicSpacing::Base02.px(cx),
|
IconSize::Small => DynamicSpacing::Base02.px(cx),
|
||||||
IconSize::Medium => DynamicSpacing::Base02.px(cx),
|
IconSize::Medium => DynamicSpacing::Base02.px(cx),
|
||||||
|
IconSize::XLarge => DynamicSpacing::Base02.px(cx),
|
||||||
};
|
};
|
||||||
|
|
||||||
(icon_size, padding)
|
(icon_size, padding)
|
||||||
|
|
|
@ -16,6 +16,7 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
activity_indicator.workspace = true
|
activity_indicator.workspace = true
|
||||||
|
zed_predict_tos.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assets.workspace = true
|
assets.workspace = true
|
||||||
assistant.workspace = true
|
assistant.workspace = true
|
||||||
|
|
|
@ -438,7 +438,11 @@ fn main() {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
snippet_provider::init(cx);
|
snippet_provider::init(cx);
|
||||||
inline_completion_registry::init(app_state.client.clone(), cx);
|
inline_completion_registry::init(
|
||||||
|
app_state.client.clone(),
|
||||||
|
app_state.user_store.clone(),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
let prompt_builder = assistant::init(
|
let prompt_builder = assistant::init(
|
||||||
app_state.fs.clone(),
|
app_state.fs.clone(),
|
||||||
app_state.client.clone(),
|
app_state.client.clone(),
|
||||||
|
|
|
@ -168,6 +168,7 @@ pub fn initialize_workspace(
|
||||||
inline_completion_button::InlineCompletionButton::new(
|
inline_completion_button::InlineCompletionButton::new(
|
||||||
workspace.weak_handle(),
|
workspace.weak_handle(),
|
||||||
app_state.fs.clone(),
|
app_state.fs.clone(),
|
||||||
|
app_state.user_store.clone(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use client::Client;
|
use client::{Client, UserStore};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use copilot::{Copilot, CopilotCompletionProvider};
|
use copilot::{Copilot, CopilotCompletionProvider};
|
||||||
use editor::{Editor, EditorMode};
|
use editor::{Editor, EditorMode};
|
||||||
use feature_flags::{FeatureFlagAppExt, PredictEditsFeatureFlag};
|
use feature_flags::{FeatureFlagAppExt, PredictEditsFeatureFlag};
|
||||||
use gpui::{AnyWindowHandle, AppContext, Context, ViewContext, WeakView};
|
use gpui::{AnyWindowHandle, AppContext, Context, Model, ViewContext, WeakView};
|
||||||
use language::language_settings::{all_language_settings, InlineCompletionProvider};
|
use language::language_settings::{all_language_settings, InlineCompletionProvider};
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use supermaven::{Supermaven, SupermavenCompletionProvider};
|
use supermaven::{Supermaven, SupermavenCompletionProvider};
|
||||||
|
use workspace::Workspace;
|
||||||
|
use zed_predict_tos::ZedPredictTos;
|
||||||
|
|
||||||
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
|
||||||
let editors: Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>> = Rc::default();
|
let editors: Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>> = Rc::default();
|
||||||
cx.observe_new_views({
|
cx.observe_new_views({
|
||||||
let editors = editors.clone();
|
let editors = editors.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
|
let user_store = user_store.clone();
|
||||||
move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
|
move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
|
||||||
if editor.mode() != EditorMode::Full {
|
if editor.mode() != EditorMode::Full {
|
||||||
return;
|
return;
|
||||||
|
@ -35,7 +38,7 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(editor_handle, cx.window_handle());
|
.insert(editor_handle, cx.window_handle());
|
||||||
let provider = all_language_settings(None, cx).inline_completions.provider;
|
let provider = all_language_settings(None, cx).inline_completions.provider;
|
||||||
assign_inline_completion_provider(editor, provider, &client, cx);
|
assign_inline_completion_provider(editor, provider, &client, user_store.clone(), cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -44,7 +47,13 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||||
for (editor, window) in editors.borrow().iter() {
|
for (editor, window) in editors.borrow().iter() {
|
||||||
_ = window.update(cx, |_window, cx| {
|
_ = window.update(cx, |_window, cx| {
|
||||||
_ = editor.update(cx, |editor, cx| {
|
_ = editor.update(cx, |editor, cx| {
|
||||||
assign_inline_completion_provider(editor, provider, &client, cx);
|
assign_inline_completion_provider(
|
||||||
|
editor,
|
||||||
|
provider,
|
||||||
|
&client,
|
||||||
|
user_store.clone(),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,9 +65,10 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||||
cx.observe_flag::<PredictEditsFeatureFlag, _>({
|
cx.observe_flag::<PredictEditsFeatureFlag, _>({
|
||||||
let editors = editors.clone();
|
let editors = editors.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
|
let user_store = user_store.clone();
|
||||||
move |active, cx| {
|
move |active, cx| {
|
||||||
let provider = all_language_settings(None, cx).inline_completions.provider;
|
let provider = all_language_settings(None, cx).inline_completions.provider;
|
||||||
assign_inline_completion_providers(&editors, provider, &client, cx);
|
assign_inline_completion_providers(&editors, provider, &client, user_store.clone(), cx);
|
||||||
if active && !cx.is_action_available(&zeta::ClearHistory) {
|
if active && !cx.is_action_available(&zeta::ClearHistory) {
|
||||||
cx.on_action(clear_zeta_edit_history);
|
cx.on_action(clear_zeta_edit_history);
|
||||||
}
|
}
|
||||||
|
@ -69,11 +79,48 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||||
cx.observe_global::<SettingsStore>({
|
cx.observe_global::<SettingsStore>({
|
||||||
let editors = editors.clone();
|
let editors = editors.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
|
let user_store = user_store.clone();
|
||||||
move |cx| {
|
move |cx| {
|
||||||
let new_provider = all_language_settings(None, cx).inline_completions.provider;
|
let new_provider = all_language_settings(None, cx).inline_completions.provider;
|
||||||
if new_provider != provider {
|
if new_provider != provider {
|
||||||
provider = new_provider;
|
provider = new_provider;
|
||||||
assign_inline_completion_providers(&editors, provider, &client, cx)
|
assign_inline_completion_providers(
|
||||||
|
&editors,
|
||||||
|
provider,
|
||||||
|
&client,
|
||||||
|
user_store.clone(),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !user_store
|
||||||
|
.read(cx)
|
||||||
|
.current_user_has_accepted_terms()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
match provider {
|
||||||
|
InlineCompletionProvider::Zed => {
|
||||||
|
let Some(window) = cx.active_window() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(workspace) = window
|
||||||
|
.downcast::<Workspace>()
|
||||||
|
.and_then(|w| w.root_view(cx).ok())
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
window
|
||||||
|
.update(cx, |_, cx| {
|
||||||
|
ZedPredictTos::toggle(workspace, user_store.clone(), cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
InlineCompletionProvider::None
|
||||||
|
| InlineCompletionProvider::Copilot
|
||||||
|
| InlineCompletionProvider::Supermaven => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -90,12 +137,19 @@ fn assign_inline_completion_providers(
|
||||||
editors: &Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>>,
|
editors: &Rc<RefCell<HashMap<WeakView<Editor>, AnyWindowHandle>>>,
|
||||||
provider: InlineCompletionProvider,
|
provider: InlineCompletionProvider,
|
||||||
client: &Arc<Client>,
|
client: &Arc<Client>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) {
|
) {
|
||||||
for (editor, window) in editors.borrow().iter() {
|
for (editor, window) in editors.borrow().iter() {
|
||||||
_ = window.update(cx, |_window, cx| {
|
_ = window.update(cx, |_window, cx| {
|
||||||
_ = editor.update(cx, |editor, cx| {
|
_ = editor.update(cx, |editor, cx| {
|
||||||
assign_inline_completion_provider(editor, provider, &client, cx);
|
assign_inline_completion_provider(
|
||||||
|
editor,
|
||||||
|
provider,
|
||||||
|
&client,
|
||||||
|
user_store.clone(),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -141,6 +195,7 @@ fn assign_inline_completion_provider(
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
provider: language::language_settings::InlineCompletionProvider,
|
provider: language::language_settings::InlineCompletionProvider,
|
||||||
client: &Arc<Client>,
|
client: &Arc<Client>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
match provider {
|
match provider {
|
||||||
|
@ -169,7 +224,7 @@ fn assign_inline_completion_provider(
|
||||||
if cx.has_flag::<PredictEditsFeatureFlag>()
|
if cx.has_flag::<PredictEditsFeatureFlag>()
|
||||||
|| (cfg!(debug_assertions) && client.status().borrow().is_connected())
|
|| (cfg!(debug_assertions) && client.status().borrow().is_connected())
|
||||||
{
|
{
|
||||||
let zeta = zeta::Zeta::register(client.clone(), cx);
|
let zeta = zeta::Zeta::register(client.clone(), user_store, cx);
|
||||||
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
|
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
|
||||||
if buffer.read(cx).file().is_some() {
|
if buffer.read(cx).file().is_some() {
|
||||||
zeta.update(cx, |zeta, cx| {
|
zeta.update(cx, |zeta, cx| {
|
||||||
|
|
23
crates/zed_predict_tos/Cargo.toml
Normal file
23
crates/zed_predict_tos/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "zed_predict_tos"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/zed_predict_tos.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-support = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
client.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
ui.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
menu.workspace = true
|
1
crates/zed_predict_tos/LICENSE-GPL
Symbolic link
1
crates/zed_predict_tos/LICENSE-GPL
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-GPL
|
152
crates/zed_predict_tos/src/zed_predict_tos.rs
Normal file
152
crates/zed_predict_tos/src/zed_predict_tos.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
//! AI service Terms of Service acceptance modal.
|
||||||
|
|
||||||
|
use client::UserStore;
|
||||||
|
use gpui::{
|
||||||
|
AppContext, ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
|
||||||
|
MouseDownEvent, Render, View,
|
||||||
|
};
|
||||||
|
use ui::{prelude::*, TintColor};
|
||||||
|
use workspace::{ModalView, Workspace};
|
||||||
|
|
||||||
|
/// Terms of acceptance for AI inline prediction.
|
||||||
|
pub struct ZedPredictTos {
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
|
workspace: View<Workspace>,
|
||||||
|
viewed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZedPredictTos {
|
||||||
|
fn new(
|
||||||
|
workspace: View<Workspace>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Self {
|
||||||
|
ZedPredictTos {
|
||||||
|
viewed: false,
|
||||||
|
focus_handle: cx.focus_handle(),
|
||||||
|
user_store,
|
||||||
|
workspace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn toggle(
|
||||||
|
workspace: View<Workspace>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) {
|
||||||
|
workspace.update(cx, |this, cx| {
|
||||||
|
let workspace = cx.view().clone();
|
||||||
|
this.toggle_modal(cx, |cx| ZedPredictTos::new(workspace, user_store, cx));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_terms(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
|
||||||
|
self.viewed = true;
|
||||||
|
cx.open_url("https://zed.dev/terms-of-service");
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accept_terms(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
|
||||||
|
let task = self
|
||||||
|
.user_store
|
||||||
|
.update(cx, |this, cx| this.accept_terms_of_service(cx));
|
||||||
|
|
||||||
|
let workspace = self.workspace.clone();
|
||||||
|
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
match task.await {
|
||||||
|
Ok(_) => this.update(&mut cx, |_, cx| {
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
}),
|
||||||
|
Err(err) => workspace.update(&mut cx, |this, cx| {
|
||||||
|
this.show_error(&err, cx);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<DismissEvent> for ZedPredictTos {}
|
||||||
|
|
||||||
|
impl FocusableView for ZedPredictTos {
|
||||||
|
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModalView for ZedPredictTos {}
|
||||||
|
|
||||||
|
impl Render for ZedPredictTos {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
v_flex()
|
||||||
|
.id("zed predict tos")
|
||||||
|
.track_focus(&self.focus_handle(cx))
|
||||||
|
.on_action(cx.listener(Self::cancel))
|
||||||
|
.key_context("ZedPredictTos")
|
||||||
|
.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, cx| {
|
||||||
|
cx.focus(&this.focus_handle);
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.justify_between()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.child(
|
||||||
|
Label::new("Zed AI")
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(Headline::new("Edit Prediction")),
|
||||||
|
)
|
||||||
|
.child(Icon::new(IconName::ZedPredict).size(IconSize::XLarge)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Label::new(
|
||||||
|
"To use Zed AI's Edit Prediction feature, please read and accept our Terms of Service.",
|
||||||
|
)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.mt_2()
|
||||||
|
.gap_0p5()
|
||||||
|
.w_full()
|
||||||
|
.child(if self.viewed {
|
||||||
|
Button::new("accept-tos", "I've Read and Accept the Terms of Service")
|
||||||
|
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||||
|
.full_width()
|
||||||
|
.on_click(cx.listener(Self::accept_terms))
|
||||||
|
} else {
|
||||||
|
Button::new("view-tos", "Read Terms of Service")
|
||||||
|
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||||
|
.icon(IconName::ArrowUpRight)
|
||||||
|
.icon_size(IconSize::XSmall)
|
||||||
|
.icon_position(IconPosition::End)
|
||||||
|
.full_width()
|
||||||
|
.on_click(cx.listener(Self::view_terms))
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
Button::new("cancel", "Cancel")
|
||||||
|
.full_width()
|
||||||
|
.on_click(cx.listener(|_, _: &ClickEvent, cx| {
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ pub use rate_completion_modal::*;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use client::Client;
|
use client::{Client, UserStore};
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
use futures::AsyncReadExt;
|
use futures::AsyncReadExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -162,6 +162,8 @@ pub struct Zeta {
|
||||||
rated_completions: HashSet<InlineCompletionId>,
|
rated_completions: HashSet<InlineCompletionId>,
|
||||||
llm_token: LlmApiToken,
|
llm_token: LlmApiToken,
|
||||||
_llm_token_subscription: Subscription,
|
_llm_token_subscription: Subscription,
|
||||||
|
tos_accepted: bool, // Terms of service accepted
|
||||||
|
_user_store_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zeta {
|
impl Zeta {
|
||||||
|
@ -169,9 +171,13 @@ impl Zeta {
|
||||||
cx.try_global::<ZetaGlobal>().map(|global| global.0.clone())
|
cx.try_global::<ZetaGlobal>().map(|global| global.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(client: Arc<Client>, cx: &mut AppContext) -> Model<Self> {
|
pub fn register(
|
||||||
|
client: Arc<Client>,
|
||||||
|
user_store: Model<UserStore>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Model<Self> {
|
||||||
Self::global(cx).unwrap_or_else(|| {
|
Self::global(cx).unwrap_or_else(|| {
|
||||||
let model = cx.new_model(|cx| Self::new(client, cx));
|
let model = cx.new_model(|cx| Self::new(client, user_store, cx));
|
||||||
cx.set_global(ZetaGlobal(model.clone()));
|
cx.set_global(ZetaGlobal(model.clone()));
|
||||||
model
|
model
|
||||||
})
|
})
|
||||||
|
@ -181,7 +187,7 @@ impl Zeta {
|
||||||
self.events.clear();
|
self.events.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
|
fn new(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut ModelContext<Self>) -> Self {
|
||||||
let refresh_llm_token_listener = language_models::RefreshLlmTokenListener::global(cx);
|
let refresh_llm_token_listener = language_models::RefreshLlmTokenListener::global(cx);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -203,6 +209,16 @@ impl Zeta {
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
tos_accepted: user_store
|
||||||
|
.read(cx)
|
||||||
|
.current_user_has_accepted_terms()
|
||||||
|
.unwrap_or(false),
|
||||||
|
_user_store_subscription: cx.subscribe(&user_store, |this, _, event, _| match event {
|
||||||
|
client::user::Event::TermsStatusUpdated { accepted } => {
|
||||||
|
this.tos_accepted = *accepted;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,6 +1037,10 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
|
||||||
settings.inline_completions_enabled(language.as_ref(), file.map(|f| f.path().as_ref()), cx)
|
settings.inline_completions_enabled(language.as_ref(), file.map(|f| f.path().as_ref()), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_terms_acceptance(&self, cx: &AppContext) -> bool {
|
||||||
|
!self.zeta.read(cx).tos_accepted
|
||||||
|
}
|
||||||
|
|
||||||
fn is_refreshing(&self) -> bool {
|
fn is_refreshing(&self) -> bool {
|
||||||
!self.pending_completions.is_empty()
|
!self.pending_completions.is_empty()
|
||||||
}
|
}
|
||||||
|
@ -1032,6 +1052,10 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
|
||||||
debounce: bool,
|
debounce: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
|
if !self.zeta.read(cx).tos_accepted {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let pending_completion_id = self.next_pending_completion_id;
|
let pending_completion_id = self.next_pending_completion_id;
|
||||||
self.next_pending_completion_id += 1;
|
self.next_pending_completion_id += 1;
|
||||||
|
|
||||||
|
@ -1337,8 +1361,9 @@ mod tests {
|
||||||
RefreshLlmTokenListener::register(client.clone(), cx);
|
RefreshLlmTokenListener::register(client.clone(), cx);
|
||||||
});
|
});
|
||||||
let server = FakeServer::for_client(42, &client, cx).await;
|
let server = FakeServer::for_client(42, &client, cx).await;
|
||||||
|
let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
|
||||||
|
let zeta = cx.new_model(|cx| Zeta::new(client, user_store, cx));
|
||||||
|
|
||||||
let zeta = cx.new_model(|cx| Zeta::new(client, cx));
|
|
||||||
let buffer = cx.new_model(|cx| Buffer::local(buffer_content, cx));
|
let buffer = cx.new_model(|cx| Buffer::local(buffer_content, cx));
|
||||||
let cursor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(1, 0)));
|
let cursor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(1, 0)));
|
||||||
let completion_task =
|
let completion_task =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue